]> git.cameronkatri.com Git - ldid.git/blob - ldid.cpp
Add proper hash agility required for 15.1+
[ldid.git] / ldid.cpp
1 /* ldid - (Mach-O) Link-Loader Identity Editor
2 * Copyright (C) 2007-2015 Jay Freeman (saurik)
3 */
4
5 /* SPDX-License-Identifier: AGPL-3.0-only */
6 /* GNU Affero General Public License, Version 3 {{{ */
7 /*
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 **/
21 /* }}} */
22
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26 #include <fstream>
27 #include <iostream>
28 #include <memory>
29 #include <set>
30 #include <sstream>
31 #include <string>
32 #include <vector>
33
34 #include <dirent.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <regex.h>
38 #include <stdbool.h>
39 #include <stdint.h>
40 #include <unistd.h>
41
42 #include <sys/mman.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45
46 #ifndef LDID_NOSMIME
47 #include <openssl/opensslv.h>
48 # if OPENSSL_VERSION_MAJOR >= 3
49 # include <openssl/provider.h>
50 # endif
51 #include <openssl/err.h>
52 #include <openssl/pem.h>
53 #include <openssl/cms.h>
54 #include <openssl/pkcs7.h>
55 #include <openssl/pkcs12.h>
56 #include <openssl/ui.h>
57 #endif
58
59 #ifdef __APPLE__
60 #include <CommonCrypto/CommonDigest.h>
61
62 #define LDID_SHA1_DIGEST_LENGTH CC_SHA1_DIGEST_LENGTH
63 #define LDID_SHA1 CC_SHA1
64 #define LDID_SHA1_CTX CC_SHA1_CTX
65 #define LDID_SHA1_Init CC_SHA1_Init
66 #define LDID_SHA1_Update CC_SHA1_Update
67 #define LDID_SHA1_Final CC_SHA1_Final
68
69 #define LDID_SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
70 #define LDID_SHA256 CC_SHA256
71 #define LDID_SHA256_CTX CC_SHA256_CTX
72 #define LDID_SHA256_Init CC_SHA256_Init
73 #define LDID_SHA256_Update CC_SHA256_Update
74 #define LDID_SHA256_Final CC_SHA256_Final
75 #else
76 #include <openssl/sha.h>
77
78 #define LDID_SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
79 #define LDID_SHA1 SHA1
80 #define LDID_SHA1_CTX SHA_CTX
81 #define LDID_SHA1_Init SHA1_Init
82 #define LDID_SHA1_Update SHA1_Update
83 #define LDID_SHA1_Final SHA1_Final
84
85 #define LDID_SHA256_DIGEST_LENGTH SHA256_DIGEST_LENGTH
86 #define LDID_SHA256 SHA256
87 #define LDID_SHA256_CTX SHA256_CTX
88 #define LDID_SHA256_Init SHA256_Init
89 #define LDID_SHA256_Update SHA256_Update
90 #define LDID_SHA256_Final SHA256_Final
91 #endif
92
93 #ifndef LDID_NOPLIST
94 #include <plist/plist.h>
95 #elif __APPLE__
96 #include <CoreFoundation/CoreFoundation.h>
97 #endif
98
99 #include "ldid.hpp"
100
101 #include "machine.h"
102
103 #define _assert___(line) \
104 #line
105 #define _assert__(line) \
106 _assert___(line)
107
108 #ifndef $
109 #define $(value) value
110 #endif
111
112 #ifdef __EXCEPTIONS
113 #define _assert_(expr, format, ...) \
114 do if (!(expr)) { \
115 fprintf(stderr, $("%s(%u): _assert(): " format "\n"), __FILE__, __LINE__, ## __VA_ARGS__); \
116 throw $(__FILE__ "(" _assert__(__LINE__) "): _assert(" #expr ")"); \
117 } while (false)
118 #else
119 // XXX: this is not acceptable
120 #define _assert_(expr, format, ...) \
121 do if (!(expr)) { \
122 fprintf(stderr, $("%s(%u): _assert(): " format "\n"), __FILE__, __LINE__, ## __VA_ARGS__); \
123 exit(-1); \
124 } while (false)
125 #endif
126
127 #define _assert(expr) \
128 _assert_(expr, "%s", $(#expr))
129
130 #define _syscall(expr, ...) [&] { for (;;) { \
131 auto _value(expr); \
132 if ((long) _value != -1) \
133 return _value; \
134 int error(errno); \
135 if (error == EINTR) \
136 continue; \
137 /* XXX: EINTR is included in this list to fix g++ */ \
138 for (auto success : (long[]) {EINTR, __VA_ARGS__}) \
139 if (error == success) \
140 return (decltype(expr)) -success; \
141 fprintf(stderr, "ldid: %s: %s\n", __func__, strerror(error)); \
142 exit(1); \
143 } }()
144
145 #define _trace() \
146 fprintf(stderr, $("_trace(%s:%u): %s\n"), __FILE__, __LINE__, $(__FUNCTION__))
147
148 #define _not(type) \
149 ((type) ~ (type) 0)
150
151 #define _packed \
152 __attribute__((packed))
153
154 #ifndef LDID_NOSMIME
155 std::string password;
156 #endif
157
158 template <typename Type_>
159 struct Iterator_ {
160 typedef typename Type_::const_iterator Result;
161 };
162
163 #define _foreach(item, list) \
164 for (bool _stop(true); _stop; ) \
165 for (const __typeof__(list) &_list = (list); _stop; _stop = false) \
166 for (Iterator_<__typeof__(list)>::Result _item = _list.begin(); _item != _list.end(); ++_item) \
167 for (bool _suck(true); _suck; _suck = false) \
168 for (const __typeof__(*_item) &item = *_item; _suck; _suck = false)
169
170 class _Scope {
171 };
172
173 template <typename Function_>
174 class Scope :
175 public _Scope
176 {
177 private:
178 Function_ function_;
179
180 public:
181 Scope(const Function_ &function) :
182 function_(function)
183 {
184 }
185
186 ~Scope() {
187 function_();
188 }
189 };
190
191 template <typename Function_>
192 Scope<Function_> _scope(const Function_ &function) {
193 return Scope<Function_>(function);
194 }
195
196 #define _scope__(counter, function) \
197 __attribute__((__unused__)) \
198 const _Scope &_scope ## counter(_scope([&]function))
199 #define _scope_(counter, function) \
200 _scope__(counter, function)
201 #define _scope(function) \
202 _scope_(__COUNTER__, function)
203
204 struct fat_header {
205 uint32_t magic;
206 uint32_t nfat_arch;
207 } _packed;
208
209 #define FAT_MAGIC 0xcafebabe
210 #define FAT_CIGAM 0xbebafeca
211
212 struct fat_arch {
213 uint32_t cputype;
214 uint32_t cpusubtype;
215 uint32_t offset;
216 uint32_t size;
217 uint32_t align;
218 } _packed;
219
220 struct mach_header {
221 uint32_t magic;
222 uint32_t cputype;
223 uint32_t cpusubtype;
224 uint32_t filetype;
225 uint32_t ncmds;
226 uint32_t sizeofcmds;
227 uint32_t flags;
228 } _packed;
229
230 #define MH_MAGIC 0xfeedface
231 #define MH_CIGAM 0xcefaedfe
232
233 #define MH_MAGIC_64 0xfeedfacf
234 #define MH_CIGAM_64 0xcffaedfe
235
236 #define MH_DYLDLINK 0x4
237
238 #define MH_OBJECT 0x1
239 #define MH_EXECUTE 0x2
240 #define MH_DYLIB 0x6
241 #define MH_DYLINKER 0x7
242 #define MH_BUNDLE 0x8
243 #define MH_DYLIB_STUB 0x9
244
245 struct load_command {
246 uint32_t cmd;
247 uint32_t cmdsize;
248 } _packed;
249
250 #define LC_REQ_DYLD uint32_t(0x80000000)
251
252 #define LC_SEGMENT uint32_t(0x01)
253 #define LC_SYMTAB uint32_t(0x02)
254 #define LC_DYSYMTAB uint32_t(0x0b)
255 #define LC_LOAD_DYLIB uint32_t(0x0c)
256 #define LC_ID_DYLIB uint32_t(0x0d)
257 #define LC_SEGMENT_64 uint32_t(0x19)
258 #define LC_UUID uint32_t(0x1b)
259 #define LC_CODE_SIGNATURE uint32_t(0x1d)
260 #define LC_SEGMENT_SPLIT_INFO uint32_t(0x1e)
261 #define LC_REEXPORT_DYLIB uint32_t(0x1f | LC_REQ_DYLD)
262 #define LC_ENCRYPTION_INFO uint32_t(0x21)
263 #define LC_DYLD_INFO uint32_t(0x22)
264 #define LC_DYLD_INFO_ONLY uint32_t(0x22 | LC_REQ_DYLD)
265 #define LC_ENCRYPTION_INFO_64 uint32_t(0x2c)
266
267 union Version {
268 struct {
269 uint8_t patch;
270 uint8_t minor;
271 uint16_t major;
272 } _packed;
273
274 uint32_t value;
275 };
276
277 struct dylib {
278 uint32_t name;
279 uint32_t timestamp;
280 uint32_t current_version;
281 uint32_t compatibility_version;
282 } _packed;
283
284 struct dylib_command {
285 uint32_t cmd;
286 uint32_t cmdsize;
287 struct dylib dylib;
288 } _packed;
289
290 struct uuid_command {
291 uint32_t cmd;
292 uint32_t cmdsize;
293 uint8_t uuid[16];
294 } _packed;
295
296 struct symtab_command {
297 uint32_t cmd;
298 uint32_t cmdsize;
299 uint32_t symoff;
300 uint32_t nsyms;
301 uint32_t stroff;
302 uint32_t strsize;
303 } _packed;
304
305 struct dyld_info_command {
306 uint32_t cmd;
307 uint32_t cmdsize;
308 uint32_t rebase_off;
309 uint32_t rebase_size;
310 uint32_t bind_off;
311 uint32_t bind_size;
312 uint32_t weak_bind_off;
313 uint32_t weak_bind_size;
314 uint32_t lazy_bind_off;
315 uint32_t lazy_bind_size;
316 uint32_t export_off;
317 uint32_t export_size;
318 } _packed;
319
320 struct dysymtab_command {
321 uint32_t cmd;
322 uint32_t cmdsize;
323 uint32_t ilocalsym;
324 uint32_t nlocalsym;
325 uint32_t iextdefsym;
326 uint32_t nextdefsym;
327 uint32_t iundefsym;
328 uint32_t nundefsym;
329 uint32_t tocoff;
330 uint32_t ntoc;
331 uint32_t modtaboff;
332 uint32_t nmodtab;
333 uint32_t extrefsymoff;
334 uint32_t nextrefsyms;
335 uint32_t indirectsymoff;
336 uint32_t nindirectsyms;
337 uint32_t extreloff;
338 uint32_t nextrel;
339 uint32_t locreloff;
340 uint32_t nlocrel;
341 } _packed;
342
343 struct dylib_table_of_contents {
344 uint32_t symbol_index;
345 uint32_t module_index;
346 } _packed;
347
348 struct dylib_module {
349 uint32_t module_name;
350 uint32_t iextdefsym;
351 uint32_t nextdefsym;
352 uint32_t irefsym;
353 uint32_t nrefsym;
354 uint32_t ilocalsym;
355 uint32_t nlocalsym;
356 uint32_t iextrel;
357 uint32_t nextrel;
358 uint32_t iinit_iterm;
359 uint32_t ninit_nterm;
360 uint32_t objc_module_info_addr;
361 uint32_t objc_module_info_size;
362 } _packed;
363
364 struct dylib_reference {
365 uint32_t isym:24;
366 uint32_t flags:8;
367 } _packed;
368
369 struct relocation_info {
370 int32_t r_address;
371 uint32_t r_symbolnum:24;
372 uint32_t r_pcrel:1;
373 uint32_t r_length:2;
374 uint32_t r_extern:1;
375 uint32_t r_type:4;
376 } _packed;
377
378 struct nlist {
379 union {
380 char *n_name;
381 int32_t n_strx;
382 } n_un;
383
384 uint8_t n_type;
385 uint8_t n_sect;
386 uint8_t n_desc;
387 uint32_t n_value;
388 } _packed;
389
390 struct segment_command {
391 uint32_t cmd;
392 uint32_t cmdsize;
393 char segname[16];
394 uint32_t vmaddr;
395 uint32_t vmsize;
396 uint32_t fileoff;
397 uint32_t filesize;
398 uint32_t maxprot;
399 uint32_t initprot;
400 uint32_t nsects;
401 uint32_t flags;
402 } _packed;
403
404 struct segment_command_64 {
405 uint32_t cmd;
406 uint32_t cmdsize;
407 char segname[16];
408 uint64_t vmaddr;
409 uint64_t vmsize;
410 uint64_t fileoff;
411 uint64_t filesize;
412 uint32_t maxprot;
413 uint32_t initprot;
414 uint32_t nsects;
415 uint32_t flags;
416 } _packed;
417
418 struct section {
419 char sectname[16];
420 char segname[16];
421 uint32_t addr;
422 uint32_t size;
423 uint32_t offset;
424 uint32_t align;
425 uint32_t reloff;
426 uint32_t nreloc;
427 uint32_t flags;
428 uint32_t reserved1;
429 uint32_t reserved2;
430 } _packed;
431
432 struct section_64 {
433 char sectname[16];
434 char segname[16];
435 uint64_t addr;
436 uint64_t size;
437 uint32_t offset;
438 uint32_t align;
439 uint32_t reloff;
440 uint32_t nreloc;
441 uint32_t flags;
442 uint32_t reserved1;
443 uint32_t reserved2;
444 uint32_t reserved3;
445 } _packed;
446
447 struct linkedit_data_command {
448 uint32_t cmd;
449 uint32_t cmdsize;
450 uint32_t dataoff;
451 uint32_t datasize;
452 } _packed;
453
454 struct encryption_info_command {
455 uint32_t cmd;
456 uint32_t cmdsize;
457 uint32_t cryptoff;
458 uint32_t cryptsize;
459 uint32_t cryptid;
460 } _packed;
461
462 #define BIND_OPCODE_MASK 0xf0
463 #define BIND_IMMEDIATE_MASK 0x0f
464 #define BIND_OPCODE_DONE 0x00
465 #define BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10
466 #define BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 0x20
467 #define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30
468 #define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40
469 #define BIND_OPCODE_SET_TYPE_IMM 0x50
470 #define BIND_OPCODE_SET_ADDEND_SLEB 0x60
471 #define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70
472 #define BIND_OPCODE_ADD_ADDR_ULEB 0x80
473 #define BIND_OPCODE_DO_BIND 0x90
474 #define BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 0xa0
475 #define BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 0xb0
476 #define BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 0xc0
477
478 struct : ldid::Progress {
479 virtual void operator()(const std::string &value) const {
480 }
481
482 virtual void operator()(double value) const {
483 }
484 } dummy_;
485
486 struct Progression : ldid::Progress {
487 const ldid::Progress &progress_;
488 std::string name_;
489
490 Progression(const ldid::Progress &progress, const std::string &name) :
491 progress_(progress),
492 name_(name)
493 {
494 }
495
496 virtual void operator()(const std::string &value) const {
497 return progress_(name_ + " (" + value + ")");
498 }
499
500 virtual void operator()(double value) const {
501 return progress_(value);
502 }
503 };
504
505 static std::streamsize read(std::streambuf &stream, void *data, size_t size) {
506 auto writ(stream.sgetn(static_cast<char *>(data), size));
507 _assert(writ >= 0);
508 return writ;
509 }
510
511 static inline void put(std::streambuf &stream, uint8_t value) {
512 _assert(stream.sputc(value) != EOF);
513 }
514
515 static inline void get(std::streambuf &stream, void *data, size_t size) {
516 _assert(read(stream, data, size) == size);
517 }
518
519 static inline void put(std::streambuf &stream, const void *data, size_t size) {
520 _assert(stream.sputn(static_cast<const char *>(data), size) == size);
521 }
522
523 static inline void put(std::streambuf &stream, const void *data, size_t size, const ldid::Progress &progress) {
524 progress(0);
525 for (size_t total(0); total != size;) {
526 auto writ(std::min(size - total, size_t(4096 * 4)));
527 _assert(stream.sputn(static_cast<const char *>(data) + total, writ) == writ);
528 total += writ;
529 progress(double(total) / size);
530 }
531 }
532
533 static inline void put(std::streambuf &stream, const std::string &data) {
534 return put(stream, data.data(), data.size());
535 }
536
537 static size_t most(std::streambuf &stream, void *data, size_t size) {
538 size_t total(size);
539 while (size > 0)
540 if (auto writ = read(stream, data, size))
541 size -= writ;
542 else break;
543 return total - size;
544 }
545
546 static inline void pad(std::streambuf &stream, size_t size) {
547 char padding[size];
548 memset(padding, 0, size);
549 put(stream, padding, size);
550 }
551
552 /*
553 * Heavily based on zsign's _GenerateASN1Type(): https://github.com/zhlynn/zsign/blob/44f15cae53e4a5a000fa7486dd72f472a4c75ee4/openssl.cpp#L116
554 * SPDX-License-Identifier: BSD-3-Clause OR AGPL-3.0-only
555 */
556 static ASN1_TYPE *GenerateASN1Type(const std::string &value)
557 {
558 std::string asn1String = "asn1=SEQUENCE:A\n[A]\nC=OBJECT:sha256\nB=FORMAT:HEX,OCT:" + value + "\n";
559
560 BIO *bio = BIO_new(BIO_s_mem());
561 BIO_puts(bio, asn1String.c_str());
562 _scope({ BIO_free(bio); });
563
564 CONF *conf = NCONF_new(NULL);
565 _scope({ NCONF_free(conf); });
566
567 long line = -1;
568 int result = NCONF_load_bio(conf, bio, &line);
569 if (result <= 0)
570 {
571 printf("Error generating ASN1 Type: %d (Line %ld)\n", result, line);
572 ERR_print_errors_fp(stdout);
573 return NULL;
574 }
575
576 char *string = NCONF_get_string(conf, "default", "asn1");
577 if (string == NULL)
578 {
579 ERR_print_errors_fp(stdout);
580 return NULL;
581 }
582
583 ASN1_TYPE *type = ASN1_generate_nconf(string, conf);
584 return type;
585 }
586
587 template <typename Type_>
588 Type_ Align(Type_ value, size_t align) {
589 value += align - 1;
590 value /= align;
591 value *= align;
592 return value;
593 }
594
595 static const uint8_t PageShift_(0x0c);
596 static const uint32_t PageSize_(1 << PageShift_);
597
598 static inline unsigned bytes(uint64_t value) {
599 return (64 - __builtin_clzll(value) + 7) / 8;
600 }
601
602 static void put(std::streambuf &stream, uint64_t value, size_t length) {
603 length *= 8;
604 do put(stream, uint8_t(value >> (length -= 8)));
605 while (length != 0);
606 }
607
608 static void der(std::streambuf &stream, uint64_t value) {
609 if (value < 128)
610 put(stream, value);
611 else {
612 unsigned length(bytes(value));
613 put(stream, 0x80 | length);
614 put(stream, value, length);
615 }
616 }
617
618 static std::string der(uint8_t tag, const char *value, size_t length) {
619 std::stringbuf data;
620 put(data, tag);
621 der(data, length);
622 put(data, value, length);
623 return data.str();
624 }
625
626 static std::string der(uint8_t tag, const char *value) {
627 return der(tag, value, strlen(value)); }
628 static std::string der(uint8_t tag, const std::string &value) {
629 return der(tag, value.data(), value.size()); }
630
631 template <typename Type_>
632 static void der_(std::stringbuf &data, const Type_ &values) {
633 size_t size(0);
634 for (const auto &value : values)
635 size += value.size();
636 der(data, size);
637 for (const auto &value : values)
638 put(data, value);
639 }
640
641 static std::string der(const std::vector<std::string> &values) {
642 std::stringbuf data;
643 put(data, 0x30);
644 der_(data, values);
645 return data.str();
646 }
647
648 static std::string der(const std::multiset<std::string> &values) {
649 std::stringbuf data;
650 put(data, 0x31);
651 der_(data, values);
652 return data.str();
653 }
654
655 static std::string der(const std::pair<std::string, std::string> &value) {
656 const auto key(der(0x0c, value.first));
657 std::stringbuf data;
658 put(data, 0x30);
659 der(data, key.size() + value.second.size());
660 put(data, key);
661 put(data, value.second);
662 return data.str();
663 }
664
665 #ifndef LDID_NOPLIST
666 static std::string der(plist_t data) {
667 switch (const auto type = plist_get_node_type(data)) {
668 case PLIST_BOOLEAN: {
669 uint8_t value(0);
670 plist_get_bool_val(data, &value);
671
672 std::stringbuf data;
673 put(data, 0x01);
674 der(data, 1);
675 put(data, value != 0 ? 1 : 0);
676 return data.str();
677 } break;
678
679 case PLIST_UINT: {
680 uint64_t value;
681 plist_get_uint_val(data, &value);
682 const auto length(bytes(value));
683
684 std::stringbuf data;
685 put(data, 0x02);
686 der(data, length);
687 put(data, value, length);
688 return data.str();
689 } break;
690
691 case PLIST_REAL: {
692 fprintf(stderr, "ldid: Invalid plist entry type\n");
693 exit(1);
694 } break;
695
696 case PLIST_DATE: {
697 fprintf(stderr, "ldid: Invalid plist entry type\n");
698 exit(1);
699 } break;
700
701 case PLIST_DATA: {
702 char *value;
703 uint64_t length;
704 plist_get_data_val(data, &value, &length);
705 _scope({ free(value); });
706 return der(0x04, value, length);
707 } break;
708
709 case PLIST_STRING: {
710 char *value;
711 plist_get_string_val(data, &value);
712 _scope({ free(value); });
713 return der(0x0c, value);
714 } break;
715
716 case PLIST_ARRAY: {
717 std::vector<std::string> values;
718 for (auto e(plist_array_get_size(data)), i(decltype(e)(0)); i != e; ++i)
719 values.push_back(der(plist_array_get_item(data, i)));
720 return der(values);
721 } break;
722
723 case PLIST_DICT: {
724 std::multiset<std::string> values;
725
726 plist_dict_iter iterator(NULL);
727 plist_dict_new_iter(data, &iterator);
728 _scope({ free(iterator); });
729
730 for (;;) {
731 char *key(NULL);
732 plist_t value(NULL);
733 plist_dict_next_item(data, iterator, &key, &value);
734 if (key == NULL)
735 break;
736 _scope({ free(key); });
737 values.insert(der(std::make_pair(key, der(value))));
738 }
739
740 return der(values);
741 } break;
742
743 default: {
744 fprintf(stderr, "ldid: Unsupported plist type %d", type);
745 exit(1);
746 } break;
747 }
748 }
749 #endif
750
751 static inline uint16_t Swap_(uint16_t value) {
752 return
753 ((value >> 8) & 0x00ff) |
754 ((value << 8) & 0xff00);
755 }
756
757 static inline uint32_t Swap_(uint32_t value) {
758 value = ((value >> 8) & 0x00ff00ff) |
759 ((value << 8) & 0xff00ff00);
760 value = ((value >> 16) & 0x0000ffff) |
761 ((value << 16) & 0xffff0000);
762 return value;
763 }
764
765 static inline uint64_t Swap_(uint64_t value) {
766 value = (value & 0x00000000ffffffff) << 32 | (value & 0xffffffff00000000) >> 32;
767 value = (value & 0x0000ffff0000ffff) << 16 | (value & 0xffff0000ffff0000) >> 16;
768 value = (value & 0x00ff00ff00ff00ff) << 8 | (value & 0xff00ff00ff00ff00) >> 8;
769 return value;
770 }
771
772 static inline int16_t Swap_(int16_t value) {
773 return Swap_(static_cast<uint16_t>(value));
774 }
775
776 static inline int32_t Swap_(int32_t value) {
777 return Swap_(static_cast<uint32_t>(value));
778 }
779
780 static inline int64_t Swap_(int64_t value) {
781 return Swap_(static_cast<uint64_t>(value));
782 }
783
784 static bool little_(true);
785
786 static inline uint16_t Swap(uint16_t value) {
787 return little_ ? Swap_(value) : value;
788 }
789
790 static inline uint32_t Swap(uint32_t value) {
791 return little_ ? Swap_(value) : value;
792 }
793
794 static inline uint64_t Swap(uint64_t value) {
795 return little_ ? Swap_(value) : value;
796 }
797
798 static inline int16_t Swap(int16_t value) {
799 return Swap(static_cast<uint16_t>(value));
800 }
801
802 static inline int32_t Swap(int32_t value) {
803 return Swap(static_cast<uint32_t>(value));
804 }
805
806 static inline int64_t Swap(int64_t value) {
807 return Swap(static_cast<uint64_t>(value));
808 }
809
810 class Swapped {
811 protected:
812 bool swapped_;
813
814 Swapped() :
815 swapped_(false)
816 {
817 }
818
819 public:
820 Swapped(bool swapped) :
821 swapped_(swapped)
822 {
823 }
824
825 template <typename Type_>
826 Type_ Swap(Type_ value) const {
827 return swapped_ ? Swap_(value) : value;
828 }
829 };
830
831 class Data :
832 public Swapped
833 {
834 private:
835 void *base_;
836 size_t size_;
837
838 public:
839 Data(void *base, size_t size) :
840 base_(base),
841 size_(size)
842 {
843 }
844
845 void *GetBase() const {
846 return base_;
847 }
848
849 size_t GetSize() const {
850 return size_;
851 }
852 };
853
854 class MachHeader :
855 public Data
856 {
857 private:
858 bool bits64_;
859
860 struct mach_header *mach_header_;
861 struct load_command *load_command_;
862
863 public:
864 MachHeader(void *base, size_t size) :
865 Data(base, size)
866 {
867 mach_header_ = (mach_header *) base;
868
869 switch (Swap(mach_header_->magic)) {
870 case MH_CIGAM:
871 swapped_ = !swapped_;
872 case MH_MAGIC:
873 bits64_ = false;
874 break;
875
876 case MH_CIGAM_64:
877 swapped_ = !swapped_;
878 case MH_MAGIC_64:
879 bits64_ = true;
880 break;
881
882 default:
883 fprintf(stderr, "ldid: Unknown header magic\nAre you sure that is a Mach-O?\n");
884 exit(1);
885 }
886
887 void *post = mach_header_ + 1;
888 if (bits64_)
889 post = (uint32_t *) post + 1;
890 load_command_ = (struct load_command *) post;
891
892 if (Swap(mach_header_->filetype) != MH_EXECUTE &&
893 Swap(mach_header_->filetype) != MH_DYLIB &&
894 Swap(mach_header_->filetype) != MH_DYLINKER &&
895 Swap(mach_header_->filetype) != MH_BUNDLE) {
896 fprintf(stderr, "ldid: Unsupported Mach-O type\n");
897 exit(1);
898 }
899 }
900
901 bool Bits64() const {
902 return bits64_;
903 }
904
905 struct mach_header *operator ->() const {
906 return mach_header_;
907 }
908
909 operator struct mach_header *() const {
910 return mach_header_;
911 }
912
913 uint32_t GetCPUType() const {
914 return Swap(mach_header_->cputype);
915 }
916
917 uint32_t GetCPUSubtype() const {
918 return Swap(mach_header_->cpusubtype) & 0xff;
919 }
920
921 struct load_command *GetLoadCommand() const {
922 return load_command_;
923 }
924
925 std::vector<struct load_command *> GetLoadCommands() const {
926 std::vector<struct load_command *> load_commands;
927
928 struct load_command *load_command = load_command_;
929 for (uint32_t cmd = 0; cmd != Swap(mach_header_->ncmds); ++cmd) {
930 load_commands.push_back(load_command);
931 load_command = (struct load_command *) ((uint8_t *) load_command + Swap(load_command->cmdsize));
932 }
933
934 return load_commands;
935 }
936
937 void ForSection(const ldid::Functor<void (const char *, const char *, void *, size_t)> &code) const {
938 _foreach (load_command, GetLoadCommands())
939 switch (Swap(load_command->cmd)) {
940 case LC_SEGMENT: {
941 auto segment(reinterpret_cast<struct segment_command *>(load_command));
942 code(segment->segname, NULL, GetOffset<void>(segment->fileoff), segment->filesize);
943 auto section(reinterpret_cast<struct section *>(segment + 1));
944 for (uint32_t i(0), e(Swap(segment->nsects)); i != e; ++i, ++section)
945 code(segment->segname, section->sectname, GetOffset<void>(segment->fileoff + section->offset), section->size);
946 } break;
947
948 case LC_SEGMENT_64: {
949 auto segment(reinterpret_cast<struct segment_command_64 *>(load_command));
950 code(segment->segname, NULL, GetOffset<void>(segment->fileoff), segment->filesize);
951 auto section(reinterpret_cast<struct section_64 *>(segment + 1));
952 for (uint32_t i(0), e(Swap(segment->nsects)); i != e; ++i, ++section)
953 code(segment->segname, section->sectname, GetOffset<void>(segment->fileoff + section->offset), section->size);
954 } break;
955 }
956 }
957
958 template <typename Target_>
959 Target_ *GetOffset(uint32_t offset) const {
960 return reinterpret_cast<Target_ *>(offset + (uint8_t *) mach_header_);
961 }
962 };
963
964 class FatMachHeader :
965 public MachHeader
966 {
967 private:
968 fat_arch *fat_arch_;
969
970 public:
971 FatMachHeader(void *base, size_t size, fat_arch *fat_arch) :
972 MachHeader(base, size),
973 fat_arch_(fat_arch)
974 {
975 }
976
977 fat_arch *GetFatArch() const {
978 return fat_arch_;
979 }
980 };
981
982 class FatHeader :
983 public Data
984 {
985 private:
986 fat_header *fat_header_;
987 std::vector<FatMachHeader> mach_headers_;
988
989 public:
990 FatHeader(void *base, size_t size) :
991 Data(base, size)
992 {
993 fat_header_ = reinterpret_cast<struct fat_header *>(base);
994
995 if (Swap(fat_header_->magic) == FAT_CIGAM) {
996 swapped_ = !swapped_;
997 goto fat;
998 } else if (Swap(fat_header_->magic) != FAT_MAGIC) {
999 fat_header_ = NULL;
1000 mach_headers_.push_back(FatMachHeader(base, size, NULL));
1001 } else fat: {
1002 size_t fat_narch = Swap(fat_header_->nfat_arch);
1003 fat_arch *fat_arch = reinterpret_cast<struct fat_arch *>(fat_header_ + 1);
1004 size_t arch;
1005 for (arch = 0; arch != fat_narch; ++arch) {
1006 uint32_t arch_offset = Swap(fat_arch->offset);
1007 uint32_t arch_size = Swap(fat_arch->size);
1008 mach_headers_.push_back(FatMachHeader((uint8_t *) base + arch_offset, arch_size, fat_arch));
1009 ++fat_arch;
1010 }
1011 }
1012 }
1013
1014 std::vector<FatMachHeader> &GetMachHeaders() {
1015 return mach_headers_;
1016 }
1017
1018 bool IsFat() const {
1019 return fat_header_ != NULL;
1020 }
1021
1022 struct fat_header *operator ->() const {
1023 return fat_header_;
1024 }
1025
1026 operator struct fat_header *() const {
1027 return fat_header_;
1028 }
1029 };
1030
1031 #define CSMAGIC_REQUIREMENT uint32_t(0xfade0c00)
1032 #define CSMAGIC_REQUIREMENTS uint32_t(0xfade0c01)
1033 #define CSMAGIC_CODEDIRECTORY uint32_t(0xfade0c02)
1034 #define CSMAGIC_EMBEDDED_SIGNATURE uint32_t(0xfade0cc0)
1035 #define CSMAGIC_EMBEDDED_SIGNATURE_OLD uint32_t(0xfade0b02)
1036 #define CSMAGIC_EMBEDDED_ENTITLEMENTS uint32_t(0xfade7171)
1037 #define CSMAGIC_EMBEDDED_DERFORMAT uint32_t(0xfade7172) // name?
1038 #define CSMAGIC_DETACHED_SIGNATURE uint32_t(0xfade0cc1)
1039 #define CSMAGIC_BLOBWRAPPER uint32_t(0xfade0b01)
1040
1041 #define CSSLOT_CODEDIRECTORY uint32_t(0x00000)
1042 #define CSSLOT_INFOSLOT uint32_t(0x00001)
1043 #define CSSLOT_REQUIREMENTS uint32_t(0x00002)
1044 #define CSSLOT_RESOURCEDIR uint32_t(0x00003)
1045 #define CSSLOT_APPLICATION uint32_t(0x00004)
1046 #define CSSLOT_ENTITLEMENTS uint32_t(0x00005)
1047 #define CSSLOT_REPSPECIFIC uint32_t(0x00006) // name?
1048 #define CSSLOT_DERFORMAT uint32_t(0x00007) // name?
1049 #define CSSLOT_ALTERNATE uint32_t(0x01000)
1050
1051 #define CSSLOT_SIGNATURESLOT uint32_t(0x10000)
1052
1053 #define CS_HASHTYPE_SHA160_160 1
1054 #define CS_HASHTYPE_SHA256_256 2
1055 #define CS_HASHTYPE_SHA256_160 3
1056 #define CS_HASHTYPE_SHA386_386 4
1057
1058 #if 0
1059 #define CS_EXECSEG_MAIN_BINARY 0x001 /* executable segment denotes main binary */
1060 #define CS_EXECSEG_ALLOW_UNSIGNED 0x010 /* allow unsigned pages (for debugging) */
1061 #define CS_EXECSEG_DEBUGGER 0x020 /* main binary is debugger */
1062 #define CS_EXECSEG_JIT 0x040 /* JIT enabled */
1063 #define CS_EXECSEG_SKIP_LV 0x080 /* skip library validation */
1064 #define CS_EXECSEG_CAN_LOAD_CDHASH 0x100 /* can bless cdhash for execution */
1065 #define CS_EXECSEG_CAN_EXEC_CDHASH 0x200 /* can execute blessed cdhash */
1066 #else
1067 enum SecCodeExecSegFlags {
1068 kSecCodeExecSegMainBinary = 0x001,
1069 kSecCodeExecSegAllowUnsigned = 0x010,
1070 kSecCodeExecSegDebugger = 0x020,
1071 kSecCodeExecSegJit = 0x040,
1072 kSecCodeExecSegSkipLibraryVal = 0x080,
1073 kSecCodeExecSegCanLoadCdHash = 0x100,
1074 kSecCodeExecSegCanExecCdHash = 0x100,
1075 };
1076 #endif
1077
1078 struct BlobIndex {
1079 uint32_t type;
1080 uint32_t offset;
1081 } _packed;
1082
1083 struct Blob {
1084 uint32_t magic;
1085 uint32_t length;
1086 } _packed;
1087
1088 struct SuperBlob {
1089 struct Blob blob;
1090 uint32_t count;
1091 struct BlobIndex index[];
1092 } _packed;
1093
1094 struct CodeDirectory {
1095 uint32_t version;
1096 uint32_t flags;
1097 uint32_t hashOffset;
1098 uint32_t identOffset;
1099 uint32_t nSpecialSlots;
1100 uint32_t nCodeSlots;
1101 uint32_t codeLimit;
1102 uint8_t hashSize;
1103 uint8_t hashType;
1104 uint8_t platform;
1105 uint8_t pageSize;
1106 uint32_t spare2;
1107 uint32_t scatterOffset;
1108 uint32_t teamIDOffset;
1109 uint32_t spare3;
1110 uint64_t codeLimit64;
1111 uint64_t execSegBase;
1112 uint64_t execSegLimit;
1113 uint64_t execSegFlags;
1114 #if 0 // version = 0x20500
1115 uint32_t runtime;
1116 uint32_t preEncryptOffset;
1117 #endif
1118 #if 0 // version = 0x20600
1119 uint8_t linkageHashType;
1120 uint8_t linkageTruncated;
1121 uint16_t spare4;
1122 uint32_t linkageOffset;
1123 uint32_t linkageSize;
1124 #endif
1125 } _packed;
1126
1127 enum CodeSignatureFlags {
1128 kSecCodeSignatureHost = 0x0001,
1129 kSecCodeSignatureAdhoc = 0x0002,
1130 kSecCodeSignatureForceHard = 0x0100,
1131 kSecCodeSignatureForceKill = 0x0200,
1132 kSecCodeSignatureForceExpiration = 0x0400,
1133 kSecCodeSignatureRestrict = 0x0800,
1134 kSecCodeSignatureEnforcement = 0x1000,
1135 kSecCodeSignatureLibraryValidation = 0x2000,
1136 kSecCodeSignatureRuntime = 0x10000,
1137 };
1138
1139 enum Kind : uint32_t {
1140 exprForm = 1, // prefix expr form
1141 };
1142
1143 enum ExprOp : uint32_t {
1144 opFalse, // unconditionally false
1145 opTrue, // unconditionally true
1146 opIdent, // match canonical code [string]
1147 opAppleAnchor, // signed by Apple as Apple's product
1148 opAnchorHash, // match anchor [cert hash]
1149 opInfoKeyValue, // *legacy* - use opInfoKeyField [key; value]
1150 opAnd, // binary prefix expr AND expr [expr; expr]
1151 opOr, // binary prefix expr OR expr [expr; expr]
1152 opCDHash, // match hash of CodeDirectory directly [cd hash]
1153 opNot, // logical inverse [expr]
1154 opInfoKeyField, // Info.plist key field [string; match suffix]
1155 opCertField, // Certificate field [cert index; field name; match suffix]
1156 opTrustedCert, // require trust settings to approve one particular cert [cert index]
1157 opTrustedCerts, // require trust settings to approve the cert chain
1158 opCertGeneric, // Certificate component by OID [cert index; oid; match suffix]
1159 opAppleGenericAnchor, // signed by Apple in any capacity
1160 opEntitlementField, // entitlement dictionary field [string; match suffix]
1161 opCertPolicy, // Certificate policy by OID [cert index; oid; match suffix]
1162 opNamedAnchor, // named anchor type
1163 opNamedCode, // named subroutine
1164 opPlatform, // platform constraint [integer]
1165 exprOpCount // (total opcode count in use)
1166 };
1167
1168 enum MatchOperation {
1169 matchExists, // anything but explicit "false" - no value stored
1170 matchEqual, // equal (CFEqual)
1171 matchContains, // partial match (substring)
1172 matchBeginsWith, // partial match (initial substring)
1173 matchEndsWith, // partial match (terminal substring)
1174 matchLessThan, // less than (string with numeric comparison)
1175 matchGreaterThan, // greater than (string with numeric comparison)
1176 matchLessEqual, // less or equal (string with numeric comparison)
1177 matchGreaterEqual, // greater or equal (string with numeric comparison)
1178 };
1179
1180 #define OID_ISO_MEMBER 42
1181 #define OID_US OID_ISO_MEMBER, 134, 72
1182 #define APPLE_OID OID_US, 0x86, 0xf7, 0x63
1183 #define APPLE_ADS_OID APPLE_OID, 0x64
1184 #define APPLE_EXTENSION_OID APPLE_ADS_OID, 6
1185
1186 #ifndef LDID_NOFLAGT
1187 extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval);
1188 #endif
1189
1190 struct Algorithm {
1191 size_t size_;
1192 uint8_t type_;
1193
1194 Algorithm(size_t size, uint8_t type) :
1195 size_(size),
1196 type_(type)
1197 {
1198 }
1199
1200 virtual const uint8_t *operator [](const ldid::Hash &hash) const = 0;
1201
1202 virtual void operator ()(uint8_t *hash, const void *data, size_t size) const = 0;
1203 virtual void operator ()(ldid::Hash &hash, const void *data, size_t size) const = 0;
1204 virtual void operator ()(std::vector<char> &hash, const void *data, size_t size) const = 0;
1205
1206 virtual const char *name() = 0;
1207 };
1208
1209 struct AlgorithmSHA1 :
1210 Algorithm
1211 {
1212 AlgorithmSHA1() :
1213 Algorithm(LDID_SHA1_DIGEST_LENGTH, CS_HASHTYPE_SHA160_160)
1214 {
1215 }
1216
1217 virtual const uint8_t *operator [](const ldid::Hash &hash) const {
1218 return hash.sha1_;
1219 }
1220
1221 void operator ()(uint8_t *hash, const void *data, size_t size) const {
1222 LDID_SHA1(static_cast<const uint8_t *>(data), size, hash);
1223 }
1224
1225 void operator ()(ldid::Hash &hash, const void *data, size_t size) const {
1226 return operator()(hash.sha1_, data, size);
1227 }
1228
1229 void operator ()(std::vector<char> &hash, const void *data, size_t size) const {
1230 hash.resize(LDID_SHA1_DIGEST_LENGTH);
1231 return operator ()(reinterpret_cast<uint8_t *>(hash.data()), data, size);
1232 }
1233
1234 virtual const char *name() {
1235 return "sha1";
1236 }
1237 };
1238
1239 struct AlgorithmSHA256 :
1240 Algorithm
1241 {
1242 AlgorithmSHA256() :
1243 Algorithm(LDID_SHA256_DIGEST_LENGTH, CS_HASHTYPE_SHA256_256)
1244 {
1245 }
1246
1247 virtual const uint8_t *operator [](const ldid::Hash &hash) const {
1248 return hash.sha256_;
1249 }
1250
1251 void operator ()(uint8_t *hash, const void *data, size_t size) const {
1252 LDID_SHA256(static_cast<const uint8_t *>(data), size, hash);
1253 }
1254
1255 void operator ()(ldid::Hash &hash, const void *data, size_t size) const {
1256 return operator()(hash.sha256_, data, size);
1257 }
1258
1259 void operator ()(std::vector<char> &hash, const void *data, size_t size) const {
1260 hash.resize(LDID_SHA256_DIGEST_LENGTH);
1261 return operator ()(reinterpret_cast<uint8_t *>(hash.data()), data, size);
1262 }
1263
1264 virtual const char *name() {
1265 return "sha256";
1266 }
1267 };
1268
1269 static bool do_sha1(true);
1270 static bool do_sha256(true);
1271
1272 static const std::vector<Algorithm *> &GetAlgorithms() {
1273 static AlgorithmSHA1 sha1;
1274 static AlgorithmSHA256 sha256;
1275
1276 static std::vector<Algorithm *> algorithms;
1277 if (algorithms.empty()) {
1278 if (do_sha1)
1279 algorithms.push_back(&sha1);
1280 if (do_sha256)
1281 algorithms.push_back(&sha256);
1282 }
1283
1284 return algorithms;
1285 }
1286
1287 struct Baton {
1288 std::string entitlements_;
1289 std::string derformat_;
1290 };
1291
1292 struct CodesignAllocation {
1293 FatMachHeader mach_header_;
1294 uint64_t offset_;
1295 uint32_t size_;
1296 uint64_t limit_;
1297 uint32_t alloc_;
1298 uint32_t align_;
1299 const char *arch_;
1300 Baton baton_;
1301
1302 CodesignAllocation(FatMachHeader mach_header, size_t offset, size_t size, size_t limit, size_t alloc, size_t align, const char *arch, const Baton &baton) :
1303 mach_header_(mach_header),
1304 offset_(offset),
1305 size_(size),
1306 limit_(limit),
1307 alloc_(alloc),
1308 align_(align),
1309 arch_(arch),
1310 baton_(baton)
1311 {
1312 }
1313 };
1314
1315 #ifndef LDID_NOTOOLS
1316 class File {
1317 private:
1318 int file_;
1319
1320 public:
1321 File() :
1322 file_(-1)
1323 {
1324 }
1325
1326 ~File() {
1327 if (file_ != -1)
1328 _syscall(close(file_));
1329 }
1330
1331 void open(const char *path, int flags) {
1332 file_ = ::open(path, flags);
1333 if (file_ == -1) {
1334 fprintf(stderr, "ldid: %s: %s\n", path, strerror(errno));
1335 exit(1);
1336 }
1337 }
1338
1339 int file() const {
1340 return file_;
1341 }
1342 };
1343
1344 class Map {
1345 private:
1346 File file_;
1347 void *data_;
1348 size_t size_;
1349
1350 void clear() {
1351 if (data_ == NULL)
1352 return;
1353 _syscall(munmap(data_, size_));
1354 data_ = NULL;
1355 size_ = 0;
1356 }
1357
1358 public:
1359 Map() :
1360 data_(NULL),
1361 size_(0)
1362 {
1363 }
1364
1365 Map(const std::string &path, int oflag, int pflag, int mflag) :
1366 Map()
1367 {
1368 open(path, oflag, pflag, mflag);
1369 }
1370
1371 Map(const std::string &path, bool edit) :
1372 Map()
1373 {
1374 open(path, edit);
1375 }
1376
1377 ~Map() {
1378 clear();
1379 }
1380
1381 bool empty() const {
1382 return data_ == NULL;
1383 }
1384
1385 void open(const std::string &path, int oflag, int pflag, int mflag) {
1386 clear();
1387
1388 file_.open(path.c_str(), oflag);
1389 int file(file_.file());
1390
1391 struct stat stat;
1392 _syscall(fstat(file, &stat));
1393 size_ = stat.st_size;
1394
1395 data_ = _syscall(mmap(NULL, size_, pflag, mflag, file, 0));
1396 }
1397
1398 void open(const std::string &path, bool edit) {
1399 if (edit)
1400 open(path, O_RDWR, PROT_READ | PROT_WRITE, MAP_SHARED);
1401 else
1402 open(path, O_RDONLY, PROT_READ, MAP_PRIVATE);
1403 }
1404
1405 void *data() const {
1406 return data_;
1407 }
1408
1409 size_t size() const {
1410 return size_;
1411 }
1412
1413 operator std::string() const {
1414 return std::string(static_cast<char *>(data_), size_);
1415 }
1416 };
1417 #endif
1418
1419 namespace ldid {
1420
1421 #ifndef LDID_NOPLIST
1422 static plist_t plist(const std::string &data);
1423 #endif
1424
1425 void Analyze(const MachHeader &mach_header, const Functor<void (const char *data, size_t size)> &entitle) {
1426 _foreach (load_command, mach_header.GetLoadCommands())
1427 if (mach_header.Swap(load_command->cmd) == LC_CODE_SIGNATURE) {
1428 auto signature(reinterpret_cast<struct linkedit_data_command *>(load_command));
1429 auto offset(mach_header.Swap(signature->dataoff));
1430 auto pointer(reinterpret_cast<uint8_t *>(mach_header.GetBase()) + offset);
1431 auto super(reinterpret_cast<struct SuperBlob *>(pointer));
1432
1433 for (size_t index(0); index != Swap(super->count); ++index)
1434 if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) {
1435 auto begin(Swap(super->index[index].offset));
1436 auto blob(reinterpret_cast<struct Blob *>(pointer + begin));
1437 auto writ(Swap(blob->length) - sizeof(*blob));
1438 entitle(reinterpret_cast<char *>(blob + 1), writ);
1439 }
1440 }
1441 }
1442
1443 std::string Analyze(const void *data, size_t size) {
1444 std::string entitlements;
1445
1446 FatHeader fat_header(const_cast<void *>(data), size);
1447 _foreach (mach_header, fat_header.GetMachHeaders())
1448 Analyze(mach_header, fun([&](const char *data, size_t size) {
1449 if (entitlements.empty())
1450 entitlements.assign(data, size);
1451 else
1452 _assert(entitlements.compare(0, entitlements.size(), data, size) == 0);
1453 }));
1454
1455 return entitlements;
1456 }
1457
1458 static void Allocate(const void *idata, size_t isize, std::streambuf &output, const Functor<size_t (const MachHeader &, Baton &, size_t)> &allocate, const Functor<size_t (const MachHeader &, const Baton &, std::streambuf &output, size_t, size_t, size_t, const std::string &, const char *, const Progress &)> &save, const Progress &progress) {
1459 FatHeader source(const_cast<void *>(idata), isize);
1460
1461 size_t offset(0);
1462 if (source.IsFat())
1463 offset += sizeof(fat_header) + sizeof(fat_arch) * source.Swap(source->nfat_arch);
1464
1465 std::vector<CodesignAllocation> allocations;
1466 _foreach (mach_header, source.GetMachHeaders()) {
1467 struct linkedit_data_command *signature(NULL);
1468 struct symtab_command *symtab(NULL);
1469
1470 _foreach (load_command, mach_header.GetLoadCommands()) {
1471 uint32_t cmd(mach_header.Swap(load_command->cmd));
1472 if (false);
1473 else if (cmd == LC_CODE_SIGNATURE)
1474 signature = reinterpret_cast<struct linkedit_data_command *>(load_command);
1475 else if (cmd == LC_SYMTAB)
1476 symtab = reinterpret_cast<struct symtab_command *>(load_command);
1477 }
1478
1479 size_t size;
1480 if (signature == NULL)
1481 size = mach_header.GetSize();
1482 else {
1483 size = mach_header.Swap(signature->dataoff);
1484 _assert(size <= mach_header.GetSize());
1485 }
1486
1487 if (symtab != NULL) {
1488 auto end(mach_header.Swap(symtab->stroff) + mach_header.Swap(symtab->strsize));
1489 if (symtab->stroff != 0 || symtab->strsize != 0) {
1490 _assert(end <= size);
1491 _assert(end >= size - 0x10);
1492 size = end;
1493 }
1494 }
1495
1496 Baton baton;
1497 size_t alloc(allocate(mach_header, baton, size));
1498
1499 auto *fat_arch(mach_header.GetFatArch());
1500 uint32_t align;
1501
1502 if (fat_arch != NULL)
1503 align = source.Swap(fat_arch->align);
1504 else switch (mach_header.GetCPUType()) {
1505 case CPU_TYPE_POWERPC:
1506 case CPU_TYPE_POWERPC64:
1507 case CPU_TYPE_X86:
1508 case CPU_TYPE_X86_64:
1509 align = 0xc;
1510 break;
1511 case CPU_TYPE_ARM:
1512 case CPU_TYPE_ARM64:
1513 case CPU_TYPE_ARM64_32:
1514 align = 0xe;
1515 break;
1516 default:
1517 align = 0x0;
1518 break;
1519 }
1520
1521 const char *arch(NULL);
1522 switch (mach_header.GetCPUType()) {
1523 case CPU_TYPE_POWERPC:
1524 arch = "ppc";
1525 break;
1526 case CPU_TYPE_POWERPC64:
1527 arch = "ppc64";
1528 break;
1529 case CPU_TYPE_X86:
1530 arch = "i386";
1531 break;
1532 case CPU_TYPE_X86_64:
1533 arch = "x86_64";
1534 break;
1535 case CPU_TYPE_ARM:
1536 arch = "arm";
1537 break;
1538 case CPU_TYPE_ARM64:
1539 arch = "arm64";
1540 break;
1541 case CPU_TYPE_ARM64_32:
1542 arch = "arm64_32";
1543 break;
1544 }
1545
1546 offset = Align(offset, 1 << align);
1547
1548 uint32_t limit(size);
1549 if (alloc != 0)
1550 limit = Align(limit, 0x10);
1551
1552 allocations.push_back(CodesignAllocation(mach_header, offset, size, limit, alloc, align, arch, baton));
1553 offset += size + alloc;
1554 offset = Align(offset, 0x10);
1555 }
1556
1557 size_t position(0);
1558
1559 if (source.IsFat()) {
1560 fat_header fat_header;
1561 fat_header.magic = Swap(FAT_MAGIC);
1562 fat_header.nfat_arch = Swap(uint32_t(allocations.size()));
1563 put(output, &fat_header, sizeof(fat_header));
1564 position += sizeof(fat_header);
1565
1566 // XXX: support fat_arch_64 (not in my toolchain)
1567 // probably use C++14 generic lambda (not in my toolchain)
1568
1569 _assert_(![&]() {
1570 _foreach (allocation, allocations) {
1571 const auto offset(allocation.offset_);
1572 const auto size(allocation.limit_ + allocation.alloc_);
1573 if (uint32_t(offset) != offset || uint32_t(size) != size)
1574 return true;
1575 }
1576 return false;
1577 }(), "FAT slice >=4GiB not currently supported");
1578
1579 _foreach (allocation, allocations) {
1580 auto &mach_header(allocation.mach_header_);
1581
1582 fat_arch fat_arch;
1583 fat_arch.cputype = Swap(mach_header->cputype);
1584 fat_arch.cpusubtype = Swap(mach_header->cpusubtype);
1585 fat_arch.offset = Swap(uint32_t(allocation.offset_));
1586 fat_arch.size = Swap(uint32_t(allocation.limit_ + allocation.alloc_));
1587 fat_arch.align = Swap(allocation.align_);
1588 put(output, &fat_arch, sizeof(fat_arch));
1589 position += sizeof(fat_arch);
1590 }
1591 }
1592
1593 _foreach (allocation, allocations) {
1594 progress(allocation.arch_);
1595 auto &mach_header(allocation.mach_header_);
1596
1597 pad(output, allocation.offset_ - position);
1598 position = allocation.offset_;
1599
1600 size_t left(-1);
1601 size_t right(0);
1602
1603 std::vector<std::string> commands;
1604
1605 _foreach (load_command, mach_header.GetLoadCommands()) {
1606 std::string copy(reinterpret_cast<const char *>(load_command), load_command->cmdsize);
1607
1608 switch (mach_header.Swap(load_command->cmd)) {
1609 case LC_CODE_SIGNATURE:
1610 continue;
1611 break;
1612
1613 // XXX: this is getting ridiculous: provide a better abstraction
1614
1615 case LC_SEGMENT: {
1616 auto segment_command(reinterpret_cast<struct segment_command *>(&copy[0]));
1617
1618 if ((segment_command->initprot & 04) != 0) {
1619 auto begin(mach_header.Swap(segment_command->fileoff));
1620 auto end(begin + mach_header.Swap(segment_command->filesize));
1621 if (left > begin)
1622 left = begin;
1623 if (right < end)
1624 right = end;
1625 }
1626
1627 if (strncmp(segment_command->segname, "__LINKEDIT", 16) == 0) {
1628 size_t size(mach_header.Swap(allocation.limit_ + allocation.alloc_ - mach_header.Swap(segment_command->fileoff)));
1629 segment_command->filesize = size;
1630 segment_command->vmsize = Align(size, 1 << allocation.align_);
1631 }
1632 } break;
1633
1634 case LC_SEGMENT_64: {
1635 auto segment_command(reinterpret_cast<struct segment_command_64 *>(&copy[0]));
1636
1637 if ((segment_command->initprot & 04) != 0) {
1638 auto begin(mach_header.Swap(segment_command->fileoff));
1639 auto end(begin + mach_header.Swap(segment_command->filesize));
1640 if (left > begin)
1641 left = begin;
1642 if (right < end)
1643 right = end;
1644 }
1645
1646 if (strncmp(segment_command->segname, "__LINKEDIT", 16) == 0) {
1647 size_t size(mach_header.Swap(allocation.limit_ + allocation.alloc_ - mach_header.Swap(segment_command->fileoff)));
1648 segment_command->filesize = size;
1649 segment_command->vmsize = Align(size, 1 << allocation.align_);
1650 }
1651 } break;
1652 }
1653
1654 commands.push_back(copy);
1655 }
1656
1657 if (allocation.alloc_ != 0) {
1658 linkedit_data_command signature;
1659 signature.cmd = mach_header.Swap(LC_CODE_SIGNATURE);
1660 signature.cmdsize = mach_header.Swap(uint32_t(sizeof(signature)));
1661 signature.dataoff = mach_header.Swap(allocation.limit_);
1662 signature.datasize = mach_header.Swap(allocation.alloc_);
1663 commands.push_back(std::string(reinterpret_cast<const char *>(&signature), sizeof(signature)));
1664 }
1665
1666 size_t begin(position);
1667
1668 uint32_t after(0);
1669 _foreach(command, commands)
1670 after += command.size();
1671
1672 std::stringbuf altern;
1673
1674 struct mach_header header(*mach_header);
1675 header.ncmds = mach_header.Swap(uint32_t(commands.size()));
1676 header.sizeofcmds = mach_header.Swap(after);
1677 put(output, &header, sizeof(header));
1678 put(altern, &header, sizeof(header));
1679 position += sizeof(header);
1680
1681 if (mach_header.Bits64()) {
1682 auto pad(mach_header.Swap(uint32_t(0)));
1683 put(output, &pad, sizeof(pad));
1684 put(altern, &pad, sizeof(pad));
1685 position += sizeof(pad);
1686 }
1687
1688 _foreach(command, commands) {
1689 put(output, command.data(), command.size());
1690 put(altern, command.data(), command.size());
1691 position += command.size();
1692 }
1693
1694 uint32_t before(mach_header.Swap(mach_header->sizeofcmds));
1695 if (before > after) {
1696 pad(output, before - after);
1697 pad(altern, before - after);
1698 position += before - after;
1699 }
1700
1701 auto top(reinterpret_cast<char *>(mach_header.GetBase()));
1702
1703 std::string overlap(altern.str());
1704 overlap.append(top + overlap.size(), Align(overlap.size(), 0x1000) - overlap.size());
1705
1706 put(output, top + (position - begin), allocation.size_ - (position - begin), progress);
1707 position = begin + allocation.size_;
1708
1709 pad(output, allocation.limit_ - allocation.size_);
1710 position += allocation.limit_ - allocation.size_;
1711
1712 size_t saved(save(mach_header, allocation.baton_, output, allocation.limit_, left, right, overlap, top, progress));
1713 if (allocation.alloc_ > saved)
1714 pad(output, allocation.alloc_ - saved);
1715 else
1716 _assert(allocation.alloc_ == saved);
1717 position += allocation.alloc_;
1718 }
1719 }
1720
1721 }
1722
1723 typedef std::map<uint32_t, std::string> Blobs;
1724
1725 static void insert(Blobs &blobs, uint32_t slot, const std::stringbuf &buffer) {
1726 auto value(buffer.str());
1727 std::swap(blobs[slot], value);
1728 }
1729
1730 static const std::string &insert(Blobs &blobs, uint32_t slot, uint32_t magic, const std::stringbuf &buffer) {
1731 auto value(buffer.str());
1732 Blob blob;
1733 blob.magic = Swap(magic);
1734 blob.length = Swap(uint32_t(sizeof(blob) + value.size()));
1735 value.insert(0, reinterpret_cast<char *>(&blob), sizeof(blob));
1736 auto &save(blobs[slot]);
1737 std::swap(save, value);
1738 return save;
1739 }
1740
1741 static size_t put(std::streambuf &output, uint32_t magic, const Blobs &blobs) {
1742 size_t total(0);
1743 _foreach (blob, blobs)
1744 total += blob.second.size();
1745
1746 struct SuperBlob super;
1747 super.blob.magic = Swap(magic);
1748 super.blob.length = Swap(uint32_t(sizeof(SuperBlob) + blobs.size() * sizeof(BlobIndex) + total));
1749 super.count = Swap(uint32_t(blobs.size()));
1750 put(output, &super, sizeof(super));
1751
1752 size_t offset(sizeof(SuperBlob) + sizeof(BlobIndex) * blobs.size());
1753
1754 _foreach (blob, blobs) {
1755 BlobIndex index;
1756 index.type = Swap(blob.first);
1757 index.offset = Swap(uint32_t(offset));
1758 put(output, &index, sizeof(index));
1759 offset += blob.second.size();
1760 }
1761
1762 _foreach (blob, blobs)
1763 put(output, blob.second.data(), blob.second.size());
1764
1765 return offset;
1766 }
1767
1768 #ifndef LDID_NOSMIME
1769 class Buffer {
1770 private:
1771 BIO *bio_;
1772
1773 public:
1774 Buffer(BIO *bio) :
1775 bio_(bio)
1776 {
1777 _assert(bio_ != NULL);
1778 }
1779
1780 Buffer() :
1781 bio_(BIO_new(BIO_s_mem()))
1782 {
1783 }
1784
1785 Buffer(const char *data, size_t size) :
1786 Buffer(BIO_new_mem_buf(const_cast<char *>(data), size))
1787 {
1788 }
1789
1790 Buffer(const std::string &data) :
1791 Buffer(data.data(), data.size())
1792 {
1793 }
1794
1795 Buffer(PKCS7 *pkcs) :
1796 Buffer()
1797 {
1798 if(i2d_PKCS7_bio(bio_, pkcs) == 0){
1799 fprintf(stderr, "ldid: An error occured while getting the PKCS12 file: %s\n", ERR_error_string(ERR_get_error(), NULL));
1800 exit(1);
1801 }
1802 }
1803
1804 Buffer(CMS_ContentInfo *cms) :
1805 Buffer()
1806 {
1807 _assert(i2d_CMS_bio(bio_, cms) != 0);
1808 }
1809
1810
1811 ~Buffer() {
1812 BIO_free_all(bio_);
1813 }
1814
1815 operator BIO *() const {
1816 return bio_;
1817 }
1818
1819 explicit operator std::string() const {
1820 char *data;
1821 auto size(BIO_get_mem_data(bio_, &data));
1822 return std::string(data, size);
1823 }
1824 };
1825
1826 class Stuff {
1827 private:
1828 PKCS12 *value_;
1829 EVP_PKEY *key_;
1830 X509 *cert_;
1831 STACK_OF(X509) *ca_;
1832
1833 public:
1834 Stuff(BIO *bio) :
1835 value_(d2i_PKCS12_bio(bio, NULL)),
1836 ca_(NULL)
1837 {
1838 if(value_ == NULL){
1839 fprintf(stderr, "ldid: An error occured while getting the PKCS12 file: %s\n", ERR_error_string(ERR_get_error(), NULL));
1840 exit(1);
1841 }
1842
1843 if (!PKCS12_verify_mac(value_, "", 0) && password.empty()) {
1844 char passbuf[2048];
1845 UI_UTIL_read_pw_string(passbuf, 2048, "Enter password: ", 0);
1846 password = passbuf;
1847 }
1848
1849 if(PKCS12_parse(value_, password.c_str(), &key_, &cert_, &ca_) <= 0){
1850 fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL));
1851 exit(1);
1852 }
1853 if(key_ == NULL || cert_ == NULL){
1854 fprintf(stderr, "ldid: An error occured while parsing: %s\nYour p12 cert might not be valid\n", ERR_error_string(ERR_get_error(), NULL));
1855 exit(1);
1856 }
1857
1858 if (ca_ == NULL)
1859 ca_ = sk_X509_new_null();
1860 if(ca_ == NULL){
1861 fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL));
1862 exit(1);
1863 }
1864 }
1865
1866 Stuff(const std::string &data) :
1867 Stuff(Buffer(data))
1868 {
1869 }
1870
1871 ~Stuff() {
1872 sk_X509_pop_free(ca_, X509_free);
1873 X509_free(cert_);
1874 EVP_PKEY_free(key_);
1875 PKCS12_free(value_);
1876 }
1877
1878 operator PKCS12 *() const {
1879 return value_;
1880 }
1881
1882 operator EVP_PKEY *() const {
1883 return key_;
1884 }
1885
1886 operator X509 *() const {
1887 return cert_;
1888 }
1889
1890 operator STACK_OF(X509) *() const {
1891 return ca_;
1892 }
1893 };
1894
1895 class Signature {
1896 private:
1897 CMS_ContentInfo *value_;
1898
1899 public:
1900 Signature(const Stuff &stuff, const Buffer &data, const std::string &xml,const std::vector<char>& alternateCDSHA256) {
1901 //
1902 int flags = CMS_PARTIAL | CMS_DETACHED | CMS_NOSMIMECAP | CMS_BINARY;
1903 //--------------------------------------------
1904 auto issuer_name(X509_get_issuer_name(stuff));
1905 _assert(issuer_name != NULL);
1906 std::string issuer;
1907 auto index(X509_NAME_get_index_by_NID(issuer_name, NID_commonName, -1));
1908 _assert(index >= 0);
1909 auto next(X509_NAME_get_index_by_NID(issuer_name, NID_commonName, index));
1910 _assert(next == -1);
1911 auto entry(X509_NAME_get_entry(issuer_name, index));
1912 _assert(entry != NULL);
1913 auto asn(X509_NAME_ENTRY_get_data(entry));
1914 _assert(asn != NULL);
1915 issuer.assign(reinterpret_cast<const char *>(ASN1_STRING_get0_data(asn)), ASN1_STRING_length(asn));
1916
1917 CMS_ContentInfo *stream = CMS_sign(NULL, NULL, stuff, NULL, flags);
1918
1919 CMS_SignerInfo *info = CMS_add1_signer(stream, stuff, stuff, EVP_sha256(), flags);
1920
1921
1922 // Hash Agility
1923 ASN1_OBJECT *obj = OBJ_txt2obj("1.2.840.113635.100.9.1", 1);
1924 CMS_signed_add1_attr_by_OBJ(info, obj, 0x4, xml.c_str(), (int)xml.size());
1925
1926 // CDHashes (iOS 15.1+)
1927 std::string sha256;
1928 for (size_t i = 0; i < alternateCDSHA256.size(); i++)
1929 {
1930 char buf[16] = {0};
1931 sprintf(buf, "%02X", (uint8_t)alternateCDSHA256[i]);
1932 sha256 += buf;
1933 }
1934
1935 X509_ATTRIBUTE *attribute = X509_ATTRIBUTE_new();
1936
1937 ASN1_OBJECT *obj2 = OBJ_txt2obj("1.2.840.113635.100.9.2", 1);
1938 X509_ATTRIBUTE_set1_object(attribute, obj2);
1939
1940 ASN1_TYPE *type256 = GenerateASN1Type(sha256);
1941 if (type256 != NULL)
1942 {
1943 X509_ATTRIBUTE_set1_data(attribute, V_ASN1_SEQUENCE, type256->value.asn1_string->data, type256->value.asn1_string->length);
1944 }
1945
1946 CMS_signed_add1_attr(info, attribute);
1947
1948 CMS_final(stream, data, NULL, flags);
1949
1950
1951 value_ = stream;
1952 _assert(value_ != NULL);
1953 }
1954
1955 ~Signature() {
1956 CMS_ContentInfo_free(value_);
1957 }
1958 operator CMS_ContentInfo *() const {
1959 return value_;
1960 }
1961 };
1962 #endif
1963
1964 class NullBuffer :
1965 public std::streambuf
1966 {
1967 public:
1968 virtual std::streamsize xsputn(const char_type *data, std::streamsize size) {
1969 return size;
1970 }
1971
1972 virtual int_type overflow(int_type next) {
1973 return next;
1974 }
1975 };
1976
1977 class HashBuffer :
1978 public std::streambuf
1979 {
1980 private:
1981 ldid::Hash &hash_;
1982
1983 LDID_SHA1_CTX sha1_;
1984 LDID_SHA256_CTX sha256_;
1985
1986 public:
1987 HashBuffer(ldid::Hash &hash) :
1988 hash_(hash)
1989 {
1990 LDID_SHA1_Init(&sha1_);
1991 LDID_SHA256_Init(&sha256_);
1992 }
1993
1994 ~HashBuffer() {
1995 LDID_SHA1_Final(reinterpret_cast<uint8_t *>(hash_.sha1_), &sha1_);
1996 LDID_SHA256_Final(reinterpret_cast<uint8_t *>(hash_.sha256_), &sha256_);
1997 }
1998
1999 virtual std::streamsize xsputn(const char_type *data, std::streamsize size) {
2000 LDID_SHA1_Update(&sha1_, data, size);
2001 LDID_SHA256_Update(&sha256_, data, size);
2002 return size;
2003 }
2004
2005 virtual int_type overflow(int_type next) {
2006 if (next == traits_type::eof())
2007 return sync();
2008 char value(next);
2009 xsputn(&value, 1);
2010 return next;
2011 }
2012 };
2013
2014 class HashProxy :
2015 public HashBuffer
2016 {
2017 private:
2018 std::streambuf &buffer_;
2019
2020 public:
2021 HashProxy(ldid::Hash &hash, std::streambuf &buffer) :
2022 HashBuffer(hash),
2023 buffer_(buffer)
2024 {
2025 }
2026
2027 virtual std::streamsize xsputn(const char_type *data, std::streamsize size) {
2028 _assert(HashBuffer::xsputn(data, size) == size);
2029 return buffer_.sputn(data, size);
2030 }
2031 };
2032
2033 #ifndef LDID_NOTOOLS
2034 static bool Starts(const std::string &lhs, const std::string &rhs) {
2035 return lhs.size() >= rhs.size() && lhs.compare(0, rhs.size(), rhs) == 0;
2036 }
2037
2038 class Split {
2039 public:
2040 std::string dir;
2041 std::string base;
2042
2043 Split(const std::string &path) {
2044 size_t slash(path.rfind('/'));
2045 if (slash == std::string::npos)
2046 base = path;
2047 else {
2048 dir = path.substr(0, slash + 1);
2049 base = path.substr(slash + 1);
2050 }
2051 }
2052 };
2053
2054 static void mkdir_p(const std::string &path) {
2055 if (path.empty())
2056 return;
2057 #ifdef __WIN32__
2058 if (_syscall(mkdir(path.c_str()), EEXIST) == -EEXIST)
2059 return;
2060 #else
2061 if (_syscall(mkdir(path.c_str(), 0755), EEXIST) == -EEXIST)
2062 return;
2063 #endif
2064 auto slash(path.rfind('/', path.size() - 1));
2065 if (slash == std::string::npos)
2066 return;
2067 mkdir_p(path.substr(0, slash));
2068 }
2069
2070 static std::string Temporary(std::filebuf &file, const Split &split) {
2071 std::string temp(split.dir + ".ldid." + split.base);
2072 mkdir_p(split.dir);
2073 _assert_(file.open(temp.c_str(), std::ios::out | std::ios::trunc | std::ios::binary) == &file, "open(): %s", temp.c_str());
2074 return temp;
2075 }
2076
2077 static void Commit(const std::string &path, const std::string &temp) {
2078 struct stat info;
2079 if (_syscall(stat(path.c_str(), &info), ENOENT) == 0) {
2080 #ifndef __WIN32__
2081 _syscall(chown(temp.c_str(), info.st_uid, info.st_gid));
2082 #endif
2083 _syscall(chmod(temp.c_str(), info.st_mode));
2084 }
2085
2086 _syscall(rename(temp.c_str(), path.c_str()));
2087 }
2088 #endif
2089
2090 namespace ldid {
2091
2092 #ifndef LDID_NOSMIME
2093 static void get(std::string &value, X509_NAME *name, int nid) {
2094 auto index(X509_NAME_get_index_by_NID(name, nid, -1));
2095 _assert(index >= 0);
2096 auto next(X509_NAME_get_index_by_NID(name, nid, index));
2097 _assert(next == -1);
2098 auto entry(X509_NAME_get_entry(name, index));
2099 _assert(entry != NULL);
2100 auto asn(X509_NAME_ENTRY_get_data(entry));
2101 _assert(asn != NULL);
2102 value.assign(reinterpret_cast<const char *>(ASN1_STRING_get0_data(asn)), ASN1_STRING_length(asn));
2103 }
2104 #endif
2105
2106 static void req(std::streambuf &buffer, uint32_t value) {
2107 value = Swap(value);
2108 put(buffer, &value, sizeof(value));
2109 }
2110
2111 static void req(std::streambuf &buffer, const std::string &value) {
2112 req(buffer, value.size());
2113 put(buffer, value.data(), value.size());
2114 static uint8_t zeros[] = {0,0,0,0};
2115 put(buffer, zeros, 3 - (value.size() + 3) % 4);
2116 }
2117
2118 template <size_t Size_>
2119 static void req(std::streambuf &buffer, uint8_t (&&data)[Size_]) {
2120 req(buffer, Size_);
2121 put(buffer, data, Size_);
2122 static uint8_t zeros[] = {0,0,0,0};
2123 put(buffer, zeros, 3 - (Size_ + 3) % 4);
2124 }
2125
2126 Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const std::string &key, const Slots &slots, uint32_t flags, bool platform, const Progress &progress) {
2127 Hash hash;
2128
2129
2130 std::string team;
2131 std::string common;
2132
2133 #ifndef LDID_NOSMIME
2134 if (!key.empty()) {
2135 Stuff stuff(key);
2136 auto name(X509_get_subject_name(stuff));
2137 if(name == NULL){
2138 fprintf(stderr, "ldid: Your certificate might not be valid: %s\n", ERR_error_string(ERR_get_error(), NULL));
2139 exit(1);
2140 }
2141 get(team, name, NID_organizationalUnitName);
2142 get(common, name, NID_commonName);
2143 }
2144 #endif
2145
2146
2147 std::stringbuf backing;
2148
2149 if (!requirements.empty()) {
2150 put(backing, requirements.data(), requirements.size());
2151 } else {
2152 Blobs blobs;
2153
2154 std::stringbuf requirement;
2155 req(requirement, exprForm);
2156 req(requirement, opAnd);
2157 req(requirement, opIdent);
2158 req(requirement, identifier);
2159 req(requirement, opAnd);
2160 req(requirement, opAppleGenericAnchor);
2161 req(requirement, opAnd);
2162 req(requirement, opCertField);
2163 req(requirement, 0);
2164 req(requirement, "subject.CN");
2165 req(requirement, matchEqual);
2166 req(requirement, common);
2167 req(requirement, opCertGeneric);
2168 req(requirement, 1);
2169 req(requirement, (uint8_t []) {APPLE_EXTENSION_OID, 2, 1});
2170 req(requirement, matchExists);
2171 insert(blobs, 3, CSMAGIC_REQUIREMENT, requirement);
2172
2173 put(backing, CSMAGIC_REQUIREMENTS, blobs);
2174 }
2175
2176
2177 // XXX: this is just a "sufficiently large number"
2178 size_t certificate(0x3000);
2179
2180 Allocate(idata, isize, output, fun([&](const MachHeader &mach_header, Baton &baton, size_t size) -> size_t {
2181 size_t alloc(sizeof(struct SuperBlob));
2182
2183 uint32_t normal((size + PageSize_ - 1) / PageSize_);
2184
2185 uint32_t special(0);
2186
2187 _foreach (slot, slots)
2188 special = std::max(special, slot.first);
2189
2190 mach_header.ForSection(fun([&](const char *segment, const char *section, void *data, size_t size) {
2191 if (strcmp(segment, "__TEXT") == 0 && section != NULL && strcmp(section, "__info_plist") == 0)
2192 special = std::max(special, CSSLOT_INFOSLOT);
2193 }));
2194
2195 special = std::max(special, CSSLOT_REQUIREMENTS);
2196 alloc += sizeof(struct BlobIndex);
2197 alloc += backing.str().size();
2198
2199 #ifdef LDID_NOPLIST
2200 baton.entitlements_ = entitlements;
2201 #else
2202 if (merge)
2203 Analyze(mach_header, fun([&](const char *data, size_t size) {
2204 baton.entitlements_.assign(data, size);
2205 }));
2206
2207 if (!baton.entitlements_.empty() || !entitlements.empty()) {
2208 auto combined(plist(baton.entitlements_));
2209 _scope({ plist_free(combined); });
2210 _assert(plist_get_node_type(combined) == PLIST_DICT);
2211
2212 auto merging(plist(entitlements));
2213 _scope({ plist_free(merging); });
2214 _assert(plist_get_node_type(merging) == PLIST_DICT);
2215
2216 plist_dict_iter iterator(NULL);
2217 plist_dict_new_iter(merging, &iterator);
2218 _scope({ free(iterator); });
2219
2220 for (;;) {
2221 char *key(NULL);
2222 plist_t value(NULL);
2223 plist_dict_next_item(merging, iterator, &key, &value);
2224 if (key == NULL)
2225 break;
2226 _scope({ free(key); });
2227 plist_dict_set_item(combined, key, plist_copy(value));
2228 }
2229
2230 baton.derformat_ = der(combined);
2231
2232 char *xml(NULL);
2233 uint32_t size;
2234 plist_to_xml(combined, &xml, &size);
2235 _scope({ free(xml); });
2236
2237 baton.entitlements_.assign(xml, size);
2238 }
2239 #endif
2240
2241 if (!baton.entitlements_.empty()) {
2242 special = std::max(special, CSSLOT_ENTITLEMENTS);
2243 alloc += sizeof(struct BlobIndex);
2244 alloc += sizeof(struct Blob);
2245 alloc += baton.entitlements_.size();
2246 }
2247
2248 if (!baton.derformat_.empty()) {
2249 special = std::max(special, CSSLOT_DERFORMAT);
2250 alloc += sizeof(struct BlobIndex);
2251 alloc += sizeof(struct Blob);
2252 alloc += baton.derformat_.size();
2253 }
2254
2255 size_t directory(0);
2256
2257 directory += sizeof(struct BlobIndex);
2258 directory += sizeof(struct Blob);
2259 directory += sizeof(struct CodeDirectory);
2260 directory += identifier.size() + 1;
2261
2262 if (!team.empty())
2263 directory += team.size() + 1;
2264
2265 for (Algorithm *algorithm : GetAlgorithms())
2266 alloc = Align(alloc + directory + (special + normal) * algorithm->size_, 16);
2267
2268 #ifndef LDID_NOSMIME
2269 if (!key.empty()) {
2270 alloc += sizeof(struct BlobIndex);
2271 alloc += sizeof(struct Blob);
2272 alloc += certificate;
2273 }
2274 #endif
2275
2276 return alloc;
2277 }), fun([&](const MachHeader &mach_header, const Baton &baton, std::streambuf &output, size_t limit, size_t left, size_t right, const std::string &overlap, const char *top, const Progress &progress) -> size_t {
2278 Blobs blobs;
2279
2280 if (true) {
2281 insert(blobs, CSSLOT_REQUIREMENTS, backing);
2282 }
2283
2284 uint64_t execs(0);
2285 if (mach_header.Swap(mach_header->filetype) == MH_EXECUTE)
2286 execs |= kSecCodeExecSegMainBinary;
2287
2288 if (!baton.entitlements_.empty()) {
2289 std::stringbuf data;
2290 put(data, baton.entitlements_.data(), baton.entitlements_.size());
2291 insert(blobs, CSSLOT_ENTITLEMENTS, CSMAGIC_EMBEDDED_ENTITLEMENTS, data);
2292
2293 #ifndef LDID_NOPLIST
2294 auto entitlements(plist(baton.entitlements_));
2295 _scope({ plist_free(entitlements); });
2296 if (plist_get_node_type(entitlements) != PLIST_DICT) {
2297 fprintf(stderr, "ldid: Entitlements should be a plist dicionary\n");
2298 exit(1);
2299 }
2300
2301 const auto entitled([&](const char *key) {
2302 auto item(plist_dict_get_item(entitlements, key));
2303 if (plist_get_node_type(item) != PLIST_BOOLEAN)
2304 return false;
2305 uint8_t value(0);
2306 plist_get_bool_val(item, &value);
2307 return value != 0;
2308 });
2309
2310 if (entitled("get-task-allow"))
2311 execs |= kSecCodeExecSegAllowUnsigned;
2312 if (entitled("run-unsigned-code"))
2313 execs |= kSecCodeExecSegAllowUnsigned;
2314 if (entitled("com.apple.private.cs.debugger"))
2315 execs |= kSecCodeExecSegDebugger;
2316 if (entitled("dynamic-codesigning"))
2317 execs |= kSecCodeExecSegJit;
2318 if (entitled("com.apple.private.skip-library-validation"))
2319 execs |= kSecCodeExecSegSkipLibraryVal;
2320 if (entitled("com.apple.private.amfi.can-load-cdhash"))
2321 execs |= kSecCodeExecSegCanLoadCdHash;
2322 if (entitled("com.apple.private.amfi.can-execute-cdhash"))
2323 execs |= kSecCodeExecSegCanExecCdHash;
2324 #endif
2325 }
2326
2327 if (!baton.derformat_.empty()) {
2328 std::stringbuf data;
2329 put(data, baton.derformat_.data(), baton.derformat_.size());
2330 insert(blobs, CSSLOT_DERFORMAT, CSMAGIC_EMBEDDED_DERFORMAT, data);
2331 }
2332
2333 Slots posts(slots);
2334
2335 mach_header.ForSection(fun([&](const char *segment, const char *section, void *data, size_t size) {
2336 if (strcmp(segment, "__TEXT") == 0 && section != NULL && strcmp(section, "__info_plist") == 0) {
2337 auto &slot(posts[CSSLOT_INFOSLOT]);
2338 for (Algorithm *algorithm : GetAlgorithms())
2339 (*algorithm)(slot, data, size);
2340 }
2341 }));
2342
2343 unsigned total(0);
2344 for (Algorithm *pointer : GetAlgorithms()) {
2345 Algorithm &algorithm(*pointer);
2346
2347 std::stringbuf data;
2348
2349 uint32_t special(0);
2350 _foreach (blob, blobs)
2351 special = std::max(special, blob.first);
2352 _foreach (slot, posts)
2353 special = std::max(special, slot.first);
2354 uint32_t normal((limit + PageSize_ - 1) / PageSize_);
2355
2356 CodeDirectory directory;
2357 directory.version = Swap(uint32_t(0x00020400));
2358 directory.flags = Swap(uint32_t(flags));
2359 directory.nSpecialSlots = Swap(special);
2360 directory.codeLimit = Swap(uint32_t(limit > UINT32_MAX ? UINT32_MAX : limit));
2361 directory.nCodeSlots = Swap(normal);
2362 directory.hashSize = algorithm.size_;
2363 directory.hashType = algorithm.type_;
2364 directory.platform = platform ? 0x01 : 0x00;
2365 directory.pageSize = PageShift_;
2366 directory.spare2 = Swap(uint32_t(0));
2367 directory.scatterOffset = Swap(uint32_t(0));
2368 directory.spare3 = Swap(uint32_t(0));
2369 directory.codeLimit64 = Swap(uint64_t(limit > UINT32_MAX ? limit : 0));
2370 directory.execSegBase = Swap(uint64_t(left));
2371 directory.execSegLimit = Swap(uint64_t(right - left));
2372 directory.execSegFlags = Swap(execs);
2373
2374 uint32_t offset(sizeof(Blob) + sizeof(CodeDirectory));
2375
2376 directory.identOffset = Swap(uint32_t(offset));
2377 offset += identifier.size() + 1;
2378
2379 if (team.empty())
2380 directory.teamIDOffset = Swap(uint32_t(0));
2381 else {
2382 directory.teamIDOffset = Swap(uint32_t(offset));
2383 offset += team.size() + 1;
2384 }
2385
2386 offset += special * algorithm.size_;
2387 directory.hashOffset = Swap(uint32_t(offset));
2388 offset += normal * algorithm.size_;
2389
2390 put(data, &directory, sizeof(directory));
2391
2392 put(data, identifier.c_str(), identifier.size() + 1);
2393 if (!team.empty())
2394 put(data, team.c_str(), team.size() + 1);
2395
2396 std::vector<uint8_t> storage((special + normal) * algorithm.size_);
2397 auto *hashes(&storage[special * algorithm.size_]);
2398
2399 memset(storage.data(), 0, special * algorithm.size_);
2400
2401 _foreach (blob, blobs) {
2402 auto local(reinterpret_cast<const Blob *>(&blob.second[0]));
2403 algorithm(hashes - blob.first * algorithm.size_, local, Swap(local->length));
2404 }
2405
2406 _foreach (slot, posts)
2407 memcpy(hashes - slot.first * algorithm.size_, algorithm[slot.second], algorithm.size_);
2408
2409 progress(0);
2410 if (normal != 1)
2411 for (size_t i = 0; i != normal - 1; ++i) {
2412 algorithm(hashes + i * algorithm.size_, (PageSize_ * i < overlap.size() ? overlap.data() : top) + PageSize_ * i, PageSize_);
2413 progress(double(i) / normal);
2414 }
2415 if (normal != 0)
2416 algorithm(hashes + (normal - 1) * algorithm.size_, top + PageSize_ * (normal - 1), ((limit - 1) % PageSize_) + 1);
2417 progress(1);
2418
2419 put(data, storage.data(), storage.size());
2420
2421 const auto &save(insert(blobs, total == 0 ? CSSLOT_CODEDIRECTORY : CSSLOT_ALTERNATE + total - 1, CSMAGIC_CODEDIRECTORY, data));
2422 algorithm(hash, save.data(), save.size());
2423
2424 ++total;
2425 }
2426
2427 #ifndef LDID_NOSMIME
2428 if (!key.empty()) {
2429 #ifdef LDID_NOPLIST
2430 auto plist(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
2431 _scope({ CFRelease(plist); });
2432
2433 auto cdhashes(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
2434 _scope({ CFRelease(cdhashes); });
2435
2436 CFDictionarySetValue(plist, CFSTR("cdhashes"), cdhashes);
2437 #else
2438 auto plist(plist_new_dict());
2439 _scope({ plist_free(plist); });
2440
2441 auto cdhashes(plist_new_array());
2442 plist_dict_set_item(plist, "cdhashes", cdhashes);
2443 #endif
2444
2445 std::vector<char> alternateCDSHA256;
2446
2447 unsigned total(0);
2448 for (Algorithm *pointer : GetAlgorithms()) {
2449 Algorithm &algorithm(*pointer);
2450 (void) algorithm;
2451
2452 const auto &blob(blobs[total == 0 ? CSSLOT_CODEDIRECTORY : CSSLOT_ALTERNATE + total - 1]);
2453 ++total;
2454
2455 std::vector<char> hash;
2456 algorithm(hash, blob.data(), blob.size());
2457 hash.resize(20);
2458
2459 if (algorithm.type_ == CS_HASHTYPE_SHA256_256){
2460 alternateCDSHA256 = hash;
2461 }
2462
2463
2464 #ifdef LDID_NOPLIST
2465 auto value(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(hash.data()), hash.size()));
2466 _scope({ CFRelease(value); });
2467 CFArrayAppendValue(cdhashes, value);
2468 #else
2469 plist_array_append_item(cdhashes, plist_new_data(hash.data(), hash.size()));
2470 #endif
2471 }
2472
2473 #ifdef LDID_NOPLIST
2474 auto created(CFPropertyListCreateXMLData(kCFAllocatorDefault, plist));
2475 _scope({ CFRelease(created); });
2476 auto xml(reinterpret_cast<const char *>(CFDataGetBytePtr(created)));
2477 auto size(CFDataGetLength(created));
2478 #else
2479 char *xml(NULL);
2480 uint32_t size;
2481 plist_to_xml(plist, &xml, &size);
2482 _scope({ free(xml); });
2483 #endif
2484
2485 std::stringbuf data;
2486 const std::string &sign(blobs[CSSLOT_CODEDIRECTORY]);
2487
2488 Stuff stuff(key);
2489 Buffer bio(sign);
2490
2491 Signature signature(stuff, sign, std::string(xml, size), alternateCDSHA256);
2492 Buffer result(signature);
2493 std::string value(result);
2494 put(data, value.data(), value.size());
2495
2496 const auto &save(insert(blobs, CSSLOT_SIGNATURESLOT, CSMAGIC_BLOBWRAPPER, data));
2497 _assert(save.size() <= certificate);
2498 }
2499 #endif
2500
2501 return put(output, CSMAGIC_EMBEDDED_SIGNATURE, blobs);
2502 }), progress);
2503
2504 return hash;
2505 }
2506
2507 #ifndef LDID_NOTOOLS
2508 static void Unsign(void *idata, size_t isize, std::streambuf &output, const Progress &progress) {
2509 Allocate(idata, isize, output, fun([](const MachHeader &mach_header, Baton &baton, size_t size) -> size_t {
2510 return 0;
2511 }), fun([](const MachHeader &mach_header, const Baton &baton, std::streambuf &output, size_t limit, size_t left, size_t right, const std::string &overlap, const char *top, const Progress &progress) -> size_t {
2512 return 0;
2513 }), progress);
2514 }
2515
2516 std::string DiskFolder::Path(const std::string &path) const {
2517 return path_ + path;
2518 }
2519
2520 DiskFolder::DiskFolder(const std::string &path) :
2521 path_(path)
2522 {
2523 _assert_(path_.size() != 0 && path_[path_.size() - 1] == '/', "missing / on %s", path_.c_str());
2524 }
2525
2526 DiskFolder::~DiskFolder() {
2527 if (!std::uncaught_exception())
2528 for (const auto &commit : commit_)
2529 Commit(commit.first, commit.second);
2530 }
2531
2532 #ifndef __WIN32__
2533 std::string readlink(const std::string &path) {
2534 for (size_t size(1024); ; size *= 2) {
2535 std::string data;
2536 data.resize(size);
2537
2538 int writ(_syscall(::readlink(path.c_str(), &data[0], data.size())));
2539 if (size_t(writ) >= size)
2540 continue;
2541
2542 data.resize(writ);
2543 return data;
2544 }
2545 }
2546 #endif
2547
2548 void DiskFolder::Find(const std::string &root, const std::string &base, const Functor<void (const std::string &)> &code, const Functor<void (const std::string &, const Functor<std::string ()> &)> &link) const {
2549 std::string path(Path(root) + base);
2550
2551 DIR *dir(opendir(path.c_str()));
2552 _assert(dir != NULL);
2553 _scope({ _syscall(closedir(dir)); });
2554
2555 while (auto child = readdir(dir)) {
2556 std::string name(child->d_name);
2557 if (name == "." || name == "..")
2558 continue;
2559 if (Starts(name, ".ldid."))
2560 continue;
2561
2562 bool directory;
2563
2564 #ifdef __WIN32__
2565 struct stat info;
2566 _syscall(stat((path + name).c_str(), &info));
2567 if (false);
2568 else if (S_ISDIR(info.st_mode))
2569 directory = true;
2570 else if (S_ISREG(info.st_mode))
2571 directory = false;
2572 else
2573 _assert_(false, "st_mode=%x", info.st_mode);
2574 #else
2575 switch (child->d_type) {
2576 case DT_DIR:
2577 directory = true;
2578 break;
2579 case DT_REG:
2580 directory = false;
2581 break;
2582 case DT_LNK:
2583 link(base + name, fun([&]() { return readlink(path + name); }));
2584 continue;
2585 default:
2586 _assert_(false, "d_type=%u", child->d_type);
2587 }
2588 #endif
2589
2590 if (directory)
2591 Find(root, base + name + "/", code, link);
2592 else
2593 code(base + name);
2594 }
2595 }
2596
2597 void DiskFolder::Save(const std::string &path, bool edit, const void *flag, const Functor<void (std::streambuf &)> &code) {
2598 if (!edit) {
2599 NullBuffer save;
2600 code(save);
2601 } else {
2602 std::filebuf save;
2603 auto from(Path(path));
2604 commit_[from] = Temporary(save, from);
2605 code(save);
2606 }
2607 }
2608
2609 bool DiskFolder::Look(const std::string &path) const {
2610 return _syscall(access(Path(path).c_str(), R_OK), ENOENT) == 0;
2611 }
2612
2613 void DiskFolder::Open(const std::string &path, const Functor<void (std::streambuf &, size_t, const void *)> &code) const {
2614 std::filebuf data;
2615 auto result(data.open(Path(path).c_str(), std::ios::binary | std::ios::in));
2616 _assert_(result == &data, "DiskFolder::Open(%s)", Path(path).c_str());
2617
2618 auto length(data.pubseekoff(0, std::ios::end, std::ios::in));
2619 data.pubseekpos(0, std::ios::in);
2620 code(data, length, NULL);
2621 }
2622
2623 void DiskFolder::Find(const std::string &path, const Functor<void (const std::string &)> &code, const Functor<void (const std::string &, const Functor<std::string ()> &)> &link) const {
2624 Find(path, "", code, link);
2625 }
2626 #endif
2627
2628 SubFolder::SubFolder(Folder &parent, const std::string &path) :
2629 parent_(parent),
2630 path_(path)
2631 {
2632 _assert_(path_.size() == 0 || path_[path_.size() - 1] == '/', "missing / on %s", path_.c_str());
2633 }
2634
2635 std::string SubFolder::Path(const std::string &path) const {
2636 return path_ + path;
2637 }
2638
2639 void SubFolder::Save(const std::string &path, bool edit, const void *flag, const Functor<void (std::streambuf &)> &code) {
2640 return parent_.Save(Path(path), edit, flag, code);
2641 }
2642
2643 bool SubFolder::Look(const std::string &path) const {
2644 return parent_.Look(Path(path));
2645 }
2646
2647 void SubFolder::Open(const std::string &path, const Functor<void (std::streambuf &, size_t, const void *)> &code) const {
2648 return parent_.Open(Path(path), code);
2649 }
2650
2651 void SubFolder::Find(const std::string &path, const Functor<void (const std::string &)> &code, const Functor<void (const std::string &, const Functor<std::string ()> &)> &link) const {
2652 return parent_.Find(Path(path), code, link);
2653 }
2654
2655 std::string UnionFolder::Map(const std::string &path) const {
2656 auto remap(remaps_.find(path));
2657 if (remap == remaps_.end())
2658 return path;
2659 return remap->second;
2660 }
2661
2662 void UnionFolder::Map(const std::string &path, const Functor<void (const std::string &)> &code, const std::string &file, const Functor<void (const Functor<void (std::streambuf &, size_t, const void *)> &)> &save) const {
2663 if (file.size() >= path.size() && file.substr(0, path.size()) == path)
2664 code(file.substr(path.size()));
2665 }
2666
2667 UnionFolder::UnionFolder(Folder &parent) :
2668 parent_(parent)
2669 {
2670 }
2671
2672 void UnionFolder::Save(const std::string &path, bool edit, const void *flag, const Functor<void (std::streambuf &)> &code) {
2673 return parent_.Save(Map(path), edit, flag, code);
2674 }
2675
2676 bool UnionFolder::Look(const std::string &path) const {
2677 auto file(resets_.find(path));
2678 if (file != resets_.end())
2679 return true;
2680 return parent_.Look(Map(path));
2681 }
2682
2683 void UnionFolder::Open(const std::string &path, const Functor<void (std::streambuf &, size_t, const void *)> &code) const {
2684 auto file(resets_.find(path));
2685 if (file == resets_.end())
2686 return parent_.Open(Map(path), code);
2687 auto &entry(file->second);
2688
2689 auto &data(*entry.data_);
2690 auto length(data.pubseekoff(0, std::ios::end, std::ios::in));
2691 data.pubseekpos(0, std::ios::in);
2692 code(data, length, entry.flag_);
2693 }
2694
2695 void UnionFolder::Find(const std::string &path, const Functor<void (const std::string &)> &code, const Functor<void (const std::string &, const Functor<std::string ()> &)> &link) const {
2696 for (auto &reset : resets_)
2697 Map(path, code, reset.first, fun([&](const Functor<void (std::streambuf &, size_t, const void *)> &code) {
2698 auto &entry(reset.second);
2699 auto &data(*entry.data_);
2700 auto length(data.pubseekoff(0, std::ios::end, std::ios::in));
2701 data.pubseekpos(0, std::ios::in);
2702 code(data, length, entry.flag_);
2703 }));
2704
2705 for (auto &remap : remaps_)
2706 Map(path, code, remap.first, fun([&](const Functor<void (std::streambuf &, size_t, const void *)> &code) {
2707 parent_.Open(remap.second, fun([&](std::streambuf &data, size_t length, const void *flag) {
2708 code(data, length, flag);
2709 }));
2710 }));
2711
2712 parent_.Find(path, fun([&](const std::string &name) {
2713 if (deletes_.find(path + name) == deletes_.end())
2714 code(name);
2715 }), fun([&](const std::string &name, const Functor<std::string ()> &read) {
2716 if (deletes_.find(path + name) == deletes_.end())
2717 link(name, read);
2718 }));
2719 }
2720
2721 #ifndef LDID_NOTOOLS
2722 static void copy(std::streambuf &source, std::streambuf &target, size_t length, const Progress &progress) {
2723 progress(0);
2724 size_t total(0);
2725 for (;;) {
2726 char data[4096 * 4];
2727 size_t writ(source.sgetn(data, sizeof(data)));
2728 if (writ == 0)
2729 break;
2730 _assert(target.sputn(data, writ) == writ);
2731 total += writ;
2732 progress(double(total) / length);
2733 }
2734 }
2735
2736 #ifndef LDID_NOPLIST
2737 static plist_t plist(const std::string &data) {
2738 if (data.empty())
2739 return plist_new_dict();
2740 plist_t plist(NULL);
2741 if (Starts(data, "bplist00"))
2742 plist_from_bin(data.data(), data.size(), &plist);
2743 else
2744 plist_from_xml(data.data(), data.size(), &plist);
2745 _assert(plist != NULL);
2746 return plist;
2747 }
2748
2749 static void plist_d(std::streambuf &buffer, size_t length, const Functor<void (plist_t)> &code) {
2750 std::stringbuf data;
2751 copy(buffer, data, length, dummy_);
2752 auto node(plist(data.str()));
2753 _scope({ plist_free(node); });
2754 _assert(plist_get_node_type(node) == PLIST_DICT);
2755 code(node);
2756 }
2757
2758 static std::string plist_s(plist_t node) {
2759 _assert(node != NULL);
2760 _assert(plist_get_node_type(node) == PLIST_STRING);
2761 char *data;
2762 plist_get_string_val(node, &data);
2763 _scope({ free(data); });
2764 return data;
2765 }
2766 #endif
2767
2768 enum Mode {
2769 NoMode,
2770 OptionalMode,
2771 OmitMode,
2772 NestedMode,
2773 TopMode,
2774 };
2775
2776 class Expression {
2777 private:
2778 regex_t regex_;
2779 std::vector<std::string> matches_;
2780
2781 public:
2782 Expression(const std::string &code) {
2783 _assert_(regcomp(&regex_, code.c_str(), REG_EXTENDED) == 0, "regcomp()");
2784 matches_.resize(regex_.re_nsub + 1);
2785 }
2786
2787 ~Expression() {
2788 regfree(&regex_);
2789 }
2790
2791 bool operator ()(const std::string &data) {
2792 regmatch_t matches[matches_.size()];
2793 auto value(regexec(&regex_, data.c_str(), matches_.size(), matches, 0));
2794 if (value == REG_NOMATCH)
2795 return false;
2796 _assert_(value == 0, "regexec()");
2797 for (size_t i(0); i != matches_.size(); ++i)
2798 matches_[i].assign(data.data() + matches[i].rm_so, matches[i].rm_eo - matches[i].rm_so);
2799 return true;
2800 }
2801
2802 const std::string &operator [](size_t index) const {
2803 return matches_[index];
2804 }
2805 };
2806
2807 struct Rule {
2808 unsigned weight_;
2809 Mode mode_;
2810 std::string code_;
2811
2812 mutable std::unique_ptr<Expression> regex_;
2813
2814 Rule(unsigned weight, Mode mode, const std::string &code) :
2815 weight_(weight),
2816 mode_(mode),
2817 code_(code)
2818 {
2819 }
2820
2821 Rule(const Rule &rhs) :
2822 weight_(rhs.weight_),
2823 mode_(rhs.mode_),
2824 code_(rhs.code_)
2825 {
2826 }
2827
2828 void Compile() const {
2829 regex_.reset(new Expression(code_));
2830 }
2831
2832 bool operator ()(const std::string &data) const {
2833 _assert(regex_.get() != NULL);
2834 return (*regex_)(data);
2835 }
2836
2837 bool operator <(const Rule &rhs) const {
2838 if (weight_ > rhs.weight_)
2839 return true;
2840 if (weight_ < rhs.weight_)
2841 return false;
2842 return mode_ > rhs.mode_;
2843 }
2844 };
2845
2846 struct RuleCode {
2847 bool operator ()(const Rule *lhs, const Rule *rhs) const {
2848 return lhs->code_ < rhs->code_;
2849 }
2850 };
2851
2852 #ifndef LDID_NOPLIST
2853 static Hash Sign(const uint8_t *prefix, size_t size, std::streambuf &buffer, Hash &hash, std::streambuf &save, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const std::string &key, const Slots &slots, size_t length, uint32_t flags, bool platform, const Progress &progress) {
2854 // XXX: this is a miserable fail
2855 std::stringbuf temp;
2856 put(temp, prefix, size);
2857 copy(buffer, temp, length - size, progress);
2858 // XXX: this is a stupid hack
2859 pad(temp, 0x10 - (length & 0xf));
2860 auto data(temp.str());
2861
2862 HashProxy proxy(hash, save);
2863 return Sign(data.data(), data.size(), proxy, identifier, entitlements, merge, requirements, key, slots, flags, platform, progress);
2864 }
2865
2866 struct State {
2867 std::map<std::string, Hash> files;
2868 std::map<std::string, std::string> links;
2869
2870 void Merge(const std::string &root, const State &state) {
2871 for (const auto &entry : state.files)
2872 files[root + entry.first] = entry.second;
2873 for (const auto &entry : state.links)
2874 links[root + entry.first] = entry.second;
2875 }
2876 };
2877
2878 Bundle Sign(const std::string &root, Folder &parent, const std::string &key, State &local, const std::string &requirements, const Functor<std::string (const std::string &, const std::string &)> &alter, const Progress &progress) {
2879 std::string executable;
2880 std::string identifier;
2881
2882 bool mac(false);
2883
2884 std::string info("Info.plist");
2885
2886 SubFolder folder(parent, [&]() {
2887 if (parent.Look(info))
2888 return "";
2889 mac = true;
2890 if (false);
2891 else if (parent.Look("Contents/" + info))
2892 return "Contents/";
2893 else if (parent.Look("Resources/" + info)) {
2894 info = "Resources/" + info;
2895 return "";
2896 } else {
2897 fprintf(stderr, "ldid: Could not find Info.plist\n");
2898 exit(1);
2899 }
2900 }());
2901
2902 folder.Open(info, fun([&](std::streambuf &buffer, size_t length, const void *flag) {
2903 plist_d(buffer, length, fun([&](plist_t node) {
2904 executable = plist_s(plist_dict_get_item(node, "CFBundleExecutable"));
2905 identifier = plist_s(plist_dict_get_item(node, "CFBundleIdentifier"));
2906 }));
2907 }));
2908
2909 if (mac && info == "Info.plist")
2910 executable = "MacOS/" + executable;
2911
2912 progress(root + "*");
2913
2914 std::string entitlements;
2915 folder.Open(executable, fun([&](std::streambuf &buffer, size_t length, const void *flag) {
2916 // XXX: this is a miserable fail
2917 std::stringbuf temp;
2918 copy(buffer, temp, length, progress);
2919 // XXX: this is a stupid hack
2920 pad(temp, 0x10 - (length & 0xf));
2921 auto data(temp.str());
2922 entitlements = alter(root, Analyze(data.data(), data.size()));
2923 }));
2924
2925 static const std::string directory("_CodeSignature/");
2926 static const std::string signature(directory + "CodeResources");
2927
2928 std::map<std::string, std::multiset<Rule>> versions;
2929
2930 auto &rules1(versions[""]);
2931 auto &rules2(versions["2"]);
2932
2933 const std::string resources(mac ? "Resources/" : "");
2934
2935 if (true) {
2936 rules1.insert(Rule{1, NoMode, "^" + (resources == "" ? ".*" : resources)});
2937 rules1.insert(Rule{1000, OptionalMode, "^" + resources + ".*\\.lproj/"});
2938 rules1.insert(Rule{1100, OmitMode, "^" + resources + ".*\\.lproj/locversion.plist$"});
2939 rules1.insert(Rule{1010, NoMode, "^" + resources + "Base\\.lproj/"});
2940 rules1.insert(Rule{1, NoMode, "^version.plist$"});
2941 }
2942
2943 if (true) {
2944 rules2.insert(Rule{11, NoMode, ".*\\.dSYM($|/)"});
2945 if (mac) rules2.insert(Rule{20, NoMode, "^" + resources});
2946 rules2.insert(Rule{2000, OmitMode, "^(.*/)?\\.DS_Store$"});
2947 if (mac) rules2.insert(Rule{10, NestedMode, "^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/"});
2948 rules2.insert(Rule{1, NoMode, "^.*"});
2949 rules2.insert(Rule{1000, OptionalMode, "^" + resources + ".*\\.lproj/"});
2950 rules2.insert(Rule{1100, OmitMode, "^" + resources + ".*\\.lproj/locversion.plist$"});
2951 if (!mac) rules2.insert(Rule{1010, NoMode, "^Base\\.lproj/"});
2952 rules2.insert(Rule{20, OmitMode, "^Info\\.plist$"});
2953 rules2.insert(Rule{20, OmitMode, "^PkgInfo$"});
2954 if (mac) rules2.insert(Rule{10, NestedMode, "^[^/]+$"});
2955 rules2.insert(Rule{20, NoMode, "^embedded\\.provisionprofile$"});
2956 if (mac) rules2.insert(Rule{1010, NoMode, "^" + resources + "Base\\.lproj/"});
2957 rules2.insert(Rule{20, NoMode, "^version\\.plist$"});
2958 }
2959
2960 std::string failure(mac ? "Contents/|Versions/[^/]*/Resources/" : "");
2961 Expression nested("^(Frameworks/[^/]*\\.framework|PlugIns/[^/]*\\.appex(()|/[^/]*.app))/(" + failure + ")Info\\.plist$");
2962 std::map<std::string, Bundle> bundles;
2963
2964 folder.Find("", fun([&](const std::string &name) {
2965 if (!nested(name))
2966 return;
2967 auto bundle(Split(name).dir);
2968 if (mac) {
2969 _assert(!bundle.empty());
2970 bundle = Split(bundle.substr(0, bundle.size() - 1)).dir;
2971 }
2972 SubFolder subfolder(folder, bundle);
2973
2974 State remote;
2975 bundles[nested[1]] = Sign(root + bundle, subfolder, key, remote, "", Starts(name, "PlugIns/") ? alter :
2976 static_cast<const Functor<std::string (const std::string &, const std::string &)> &>(fun([&](const std::string &, const std::string &) -> std::string { return entitlements; }))
2977 , progress);
2978 local.Merge(bundle, remote);
2979 }), fun([&](const std::string &name, const Functor<std::string ()> &read) {
2980 }));
2981
2982 std::set<std::string> excludes;
2983
2984 auto exclude([&](const std::string &name) {
2985 // BundleDiskRep::adjustResources -> builder.addExclusion
2986 if (name == executable || Starts(name, directory) || Starts(name, "_MASReceipt/") || name == "CodeResources")
2987 return true;
2988
2989 for (const auto &bundle : bundles)
2990 if (Starts(name, bundle.first + "/")) {
2991 excludes.insert(name);
2992 return true;
2993 }
2994
2995 return false;
2996 });
2997
2998 folder.Find("", fun([&](const std::string &name) {
2999 if (exclude(name))
3000 return;
3001
3002 if (local.files.find(name) != local.files.end())
3003 return;
3004 auto &hash(local.files[name]);
3005
3006 folder.Open(name, fun([&](std::streambuf &data, size_t length, const void *flag) {
3007 progress(root + name);
3008
3009 union {
3010 struct {
3011 uint32_t magic;
3012 uint32_t count;
3013 };
3014
3015 uint8_t bytes[8];
3016 } header;
3017
3018 auto size(most(data, &header.bytes, sizeof(header.bytes)));
3019
3020 if (name != "_WatchKitStub/WK" && size == sizeof(header.bytes))
3021 switch (Swap(header.magic)) {
3022 case FAT_MAGIC:
3023 // Java class file format
3024 if (Swap(header.count) >= 40)
3025 break;
3026 case FAT_CIGAM:
3027 case MH_MAGIC: case MH_MAGIC_64:
3028 case MH_CIGAM: case MH_CIGAM_64:
3029 folder.Save(name, true, flag, fun([&](std::streambuf &save) {
3030 Slots slots;
3031 Sign(header.bytes, size, data, hash, save, identifier, "", false, "", key, slots, length, 0, false, Progression(progress, root + name));
3032 }));
3033 return;
3034 }
3035
3036 folder.Save(name, false, flag, fun([&](std::streambuf &save) {
3037 HashProxy proxy(hash, save);
3038 put(proxy, header.bytes, size);
3039 copy(data, proxy, length - size, progress);
3040 }));
3041 }));
3042 }), fun([&](const std::string &name, const Functor<std::string ()> &read) {
3043 if (exclude(name))
3044 return;
3045
3046 local.links[name] = read();
3047 }));
3048
3049 auto plist(plist_new_dict());
3050 _scope({ plist_free(plist); });
3051
3052 for (const auto &version : versions) {
3053 auto files(plist_new_dict());
3054 plist_dict_set_item(plist, ("files" + version.first).c_str(), files);
3055
3056 for (const auto &rule : version.second)
3057 rule.Compile();
3058
3059 bool old(&version.second == &rules1);
3060
3061 for (const auto &hash : local.files)
3062 for (const auto &rule : version.second)
3063 if (rule(hash.first)) {
3064 if (!old && mac && excludes.find(hash.first) != excludes.end());
3065 else if (old && rule.mode_ == NoMode)
3066 plist_dict_set_item(files, hash.first.c_str(), plist_new_data(reinterpret_cast<const char *>(hash.second.sha1_), sizeof(hash.second.sha1_)));
3067 else if (rule.mode_ != OmitMode) {
3068 auto entry(plist_new_dict());
3069 plist_dict_set_item(entry, "hash", plist_new_data(reinterpret_cast<const char *>(hash.second.sha1_), sizeof(hash.second.sha1_)));
3070 if (!old)
3071 plist_dict_set_item(entry, "hash2", plist_new_data(reinterpret_cast<const char *>(hash.second.sha256_), sizeof(hash.second.sha256_)));
3072 if (rule.mode_ == OptionalMode)
3073 plist_dict_set_item(entry, "optional", plist_new_bool(true));
3074 plist_dict_set_item(files, hash.first.c_str(), entry);
3075 }
3076
3077 break;
3078 }
3079
3080 if (!old)
3081 for (const auto &link : local.links)
3082 for (const auto &rule : version.second)
3083 if (rule(link.first)) {
3084 if (rule.mode_ != OmitMode) {
3085 auto entry(plist_new_dict());
3086 plist_dict_set_item(entry, "symlink", plist_new_string(link.second.c_str()));
3087 if (rule.mode_ == OptionalMode)
3088 plist_dict_set_item(entry, "optional", plist_new_bool(true));
3089 plist_dict_set_item(files, link.first.c_str(), entry);
3090 }
3091
3092 break;
3093 }
3094
3095 if (!old && mac)
3096 for (const auto &bundle : bundles) {
3097 auto entry(plist_new_dict());
3098 plist_dict_set_item(entry, "cdhash", plist_new_data(reinterpret_cast<const char *>(bundle.second.hash.sha256_), sizeof(bundle.second.hash.sha256_)));
3099 plist_dict_set_item(entry, "requirement", plist_new_string("anchor apple generic"));
3100 plist_dict_set_item(files, bundle.first.c_str(), entry);
3101 }
3102 }
3103
3104 for (const auto &version : versions) {
3105 auto rules(plist_new_dict());
3106 plist_dict_set_item(plist, ("rules" + version.first).c_str(), rules);
3107
3108 std::multiset<const Rule *, RuleCode> ordered;
3109 for (const auto &rule : version.second)
3110 ordered.insert(&rule);
3111
3112 for (const auto &rule : ordered)
3113 if (rule->weight_ == 1 && rule->mode_ == NoMode)
3114 plist_dict_set_item(rules, rule->code_.c_str(), plist_new_bool(true));
3115 else {
3116 auto entry(plist_new_dict());
3117 plist_dict_set_item(rules, rule->code_.c_str(), entry);
3118
3119 switch (rule->mode_) {
3120 case NoMode:
3121 break;
3122 case OmitMode:
3123 plist_dict_set_item(entry, "omit", plist_new_bool(true));
3124 break;
3125 case OptionalMode:
3126 plist_dict_set_item(entry, "optional", plist_new_bool(true));
3127 break;
3128 case NestedMode:
3129 plist_dict_set_item(entry, "nested", plist_new_bool(true));
3130 break;
3131 case TopMode:
3132 plist_dict_set_item(entry, "top", plist_new_bool(true));
3133 break;
3134 }
3135
3136 if (rule->weight_ >= 10000)
3137 plist_dict_set_item(entry, "weight", plist_new_uint(rule->weight_));
3138 else if (rule->weight_ != 1)
3139 plist_dict_set_item(entry, "weight", plist_new_real(rule->weight_));
3140 }
3141 }
3142
3143 folder.Save(signature, true, NULL, fun([&](std::streambuf &save) {
3144 HashProxy proxy(local.files[signature], save);
3145 char *xml(NULL);
3146 uint32_t size;
3147 plist_to_xml(plist, &xml, &size);
3148 _scope({ free(xml); });
3149 put(proxy, xml, size);
3150 }));
3151
3152 Bundle bundle;
3153 bundle.path = folder.Path(executable);
3154
3155 folder.Open(executable, fun([&](std::streambuf &buffer, size_t length, const void *flag) {
3156 progress(root + executable);
3157 folder.Save(executable, true, flag, fun([&](std::streambuf &save) {
3158 Slots slots;
3159 slots[1] = local.files.at(info);
3160 slots[3] = local.files.at(signature);
3161 bundle.hash = Sign(NULL, 0, buffer, local.files[executable], save, identifier, entitlements, false, requirements, key, slots, length, 0, false, Progression(progress, root + executable));
3162 }));
3163 }));
3164
3165 return bundle;
3166 }
3167
3168 Bundle Sign(const std::string &root, Folder &folder, const std::string &key, const std::string &requirements, const Functor<std::string (const std::string &, const std::string &)> &alter, const Progress &progress) {
3169 State local;
3170 return Sign(root, folder, key, local, requirements, alter, progress);
3171 }
3172 #endif
3173
3174 #endif
3175 }
3176
3177 std::string Hex(const uint8_t *data, size_t size) {
3178 std::string hex;
3179 hex.reserve(size * 2);
3180 for (size_t i(0); i != size; ++i) {
3181 hex += "0123456789abcdef"[data[i] >> 4];
3182 hex += "0123456789abcdef"[data[i] & 0xf];
3183 }
3184 return hex;
3185 }
3186
3187 static void usage(const char *argv0) {
3188 fprintf(stderr, "Link Identity Editor %s\n\n", LDID_VERSION);
3189 fprintf(stderr, "Usage: %s [-Acputype:subtype] [-a] [-C[adhoc | enforcement | expires | hard |\n", argv0);
3190 fprintf(stderr, " host | kill | library-validation | restrict | runtime]] [-D] [-d]\n");
3191 fprintf(stderr, " [-Enum:file] [-e] [-H[sha1 | sha256]] [-h] [-Iname]\n");
3192 fprintf(stderr, " [-Kkey.p12 [-Upassword]] [-M] [-P] [-Qrequirements.xml] [-q]\n");
3193 fprintf(stderr, " [-r | -Sfile.xml | -s] [-Ttimestamp] [-u] [-arch arch_type] file ...\n");
3194 fprintf(stderr, "Options:\n");
3195 fprintf(stderr, " -S[file.xml] Pseudo-sign using the entitlements in file.xml\n");
3196 fprintf(stderr, " -Kkey.p12 Sign using private key in key.p12\n");
3197 fprintf(stderr, " -Upassword Use password to unlock key.p12\n");
3198 fprintf(stderr, " -M Merge entitlements with any existing\n");
3199 fprintf(stderr, " -h Print CDHash of file\n\n");
3200 fprintf(stderr, "More information: 'man ldid'\n");
3201 }
3202
3203 #ifndef LDID_NOTOOLS
3204 int main(int argc, char *argv[]) {
3205 #ifndef LDID_NOSMIME
3206 OpenSSL_add_all_algorithms();
3207 # if OPENSSL_VERSION_MAJOR >= 3
3208 OSSL_PROVIDER *legacy = OSSL_PROVIDER_load(NULL, "legacy");
3209 OSSL_PROVIDER *deflt = OSSL_PROVIDER_load(NULL, "default");
3210 # endif
3211 #endif
3212
3213 union {
3214 uint16_t word;
3215 uint8_t byte[2];
3216 } endian = {1};
3217
3218 little_ = endian.byte[0];
3219
3220 bool flag_r(false);
3221 bool flag_e(false);
3222 bool flag_q(false);
3223
3224 bool flag_H(false);
3225 bool flag_h(false);
3226
3227 #ifndef LDID_NOFLAGT
3228 bool flag_T(false);
3229 #endif
3230
3231 bool flag_S(false);
3232 bool flag_s(false);
3233
3234 bool flag_D(false);
3235 bool flag_d(false);
3236
3237 bool flag_A(false);
3238 bool flag_a(false);
3239
3240 bool flag_u(false);
3241
3242 bool flag_M(false);
3243
3244 uint32_t flags(0);
3245 bool platform(false);
3246
3247 uint32_t flag_CPUType(_not(uint32_t));
3248 uint32_t flag_CPUSubtype(_not(uint32_t));
3249
3250 const char *flag_I(NULL);
3251
3252 #ifndef LDID_NOFLAGT
3253 bool timeh(false);
3254 uint32_t timev(0);
3255 #endif
3256
3257 Map entitlements;
3258 Map requirements;
3259 Map key;
3260 ldid::Slots slots;
3261
3262 std::vector<std::string> files;
3263
3264 if (argc == 1) {
3265 usage(argv[0]);
3266 return 0;
3267 }
3268
3269 for (int argi(1); argi != argc; ++argi)
3270 if (argv[argi][0] != '-')
3271 files.push_back(argv[argi]);
3272 else if (strcmp(argv[argi], "-arch") == 0) {
3273 bool foundarch = false;
3274 flag_A = true;
3275 argi++;
3276 if (argi == argc) {
3277 fprintf(stderr, "ldid: -arch must be followed by an architecture string\n");
3278 exit(1);
3279 }
3280 for (int i = 0; archs[i].name != NULL; i++) {
3281 if (strcmp(archs[i].name, argv[argi]) == 0) {
3282 flag_CPUType = archs[i].cputype;
3283 flag_CPUSubtype = archs[i].cpusubtype;
3284 foundarch = true;
3285 }
3286 if (foundarch)
3287 break;
3288 }
3289
3290 if (!foundarch) {
3291 fprintf(stderr, "error: unknown architecture specification flag: -arch %s\n", argv[argi]);
3292 exit(1);
3293 }
3294 } else switch (argv[argi][1]) {
3295 case 'r':
3296 if (flag_s || flag_S) {
3297 fprintf(stderr, "ldid: Can only specify one of -r, -S, -s\n");
3298 exit(1);
3299 }
3300 flag_r = true;
3301 break;
3302
3303 case 'e': flag_e = true; break;
3304
3305 case 'E': {
3306 const char *string = argv[argi] + 2;
3307 const char *colon = strchr(string, ':');
3308 _assert(colon != NULL);
3309 Map file(colon + 1, O_RDONLY, PROT_READ, MAP_PRIVATE);
3310 char *arge;
3311 unsigned number(strtoul(string, &arge, 0));
3312 _assert(arge == colon);
3313 auto &slot(slots[number]);
3314 for (Algorithm *algorithm : GetAlgorithms())
3315 (*algorithm)(slot, file.data(), file.size());
3316 } break;
3317
3318 case 'q': flag_q = true; break;
3319
3320 case 'H': {
3321 const char *hash = argv[argi] + 2;
3322
3323 if (!flag_H) {
3324 flag_H = true;
3325
3326 do_sha1 = false;
3327 do_sha256 = false;
3328 }
3329
3330 if (false);
3331 else if (strcmp(hash, "sha1") == 0)
3332 do_sha1 = true;
3333 else if (strcmp(hash, "sha256") == 0)
3334 do_sha256 = true;
3335 else {
3336 fprintf(stderr, "ldid: only sha1 and sha256 are supported at this time\n");
3337 exit(1);
3338 }
3339 } break;
3340
3341 case 'h': flag_h = true; break;
3342
3343 case 'Q': {
3344 const char *xml = argv[argi] + 2;
3345 requirements.open(xml, O_RDONLY, PROT_READ, MAP_PRIVATE);
3346 } break;
3347
3348 case 'D': flag_D = true; break;
3349 case 'd': flag_d = true; break;
3350
3351 case 'a': flag_a = true; break;
3352
3353 case 'A':
3354 if (flag_A) {
3355 fprintf(stderr, "ldid: -A can only be specified once\n");
3356 exit(1);
3357 }
3358 flag_A = true;
3359 if (argv[argi][2] != '\0') {
3360 const char *cpu = argv[argi] + 2;
3361 const char *colon = strchr(cpu, ':');
3362 _assert(colon != NULL);
3363 char *arge;
3364 flag_CPUType = strtoul(cpu, &arge, 0);
3365 _assert(arge == colon);
3366 flag_CPUSubtype = strtoul(colon + 1, &arge, 0);
3367 _assert(arge == argv[argi] + strlen(argv[argi]));
3368 }
3369 break;
3370
3371 case 'C': {
3372 const char *name = argv[argi] + 2;
3373 if (false);
3374 else if (strcmp(name, "host") == 0)
3375 flags |= kSecCodeSignatureHost;
3376 else if (strcmp(name, "adhoc") == 0)
3377 flags |= kSecCodeSignatureAdhoc;
3378 else if (strcmp(name, "hard") == 0)
3379 flags |= kSecCodeSignatureForceHard;
3380 else if (strcmp(name, "kill") == 0)
3381 flags |= kSecCodeSignatureForceKill;
3382 else if (strcmp(name, "expires") == 0)
3383 flags |= kSecCodeSignatureForceExpiration;
3384 else if (strcmp(name, "restrict") == 0)
3385 flags |= kSecCodeSignatureRestrict;
3386 else if (strcmp(name, "enforcement") == 0)
3387 flags |= kSecCodeSignatureEnforcement;
3388 else if (strcmp(name, "library-validation") == 0)
3389 flags |= kSecCodeSignatureLibraryValidation;
3390 else if (strcmp(name, "runtime") == 0)
3391 flags |= kSecCodeSignatureRuntime;
3392 else {
3393 fprintf(stderr, "ldid: -C: Unsupported option\n");
3394 exit(1);
3395 }
3396 } break;
3397
3398 case 'P':
3399 platform = true;
3400 break;
3401
3402 case 's':
3403 if (flag_r || flag_S) {
3404 fprintf(stderr, "ldid: Can only specify one of -r, -S, -s\n");
3405 exit(1);
3406 }
3407 flag_s = true;
3408 break;
3409
3410 case 'S':
3411 if (flag_r || flag_s) {
3412 fprintf(stderr, "ldid: Can only specify one of -r, -S, -s\n");
3413 exit(1);
3414 }
3415 flag_S = true;
3416 if (argv[argi][2] != '\0') {
3417 const char *xml = argv[argi] + 2;
3418 entitlements.open(xml, O_RDONLY, PROT_READ, MAP_PRIVATE);
3419 }
3420 break;
3421
3422 case 'M':
3423 flag_M = true;
3424 break;
3425
3426 case 'U':
3427 password = argv[argi] + 2;
3428 break;
3429
3430 case 'K':
3431 if (argv[argi][2] != '\0')
3432 key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE);
3433 break;
3434
3435 #ifndef LDID_NOFLAGT
3436 case 'T': {
3437 flag_T = true;
3438 if (argv[argi][2] == '-')
3439 timeh = true;
3440 else {
3441 char *arge;
3442 timev = strtoul(argv[argi] + 2, &arge, 0);
3443 _assert(arge == argv[argi] + strlen(argv[argi]));
3444 }
3445 } break;
3446 #endif
3447
3448 case 'u': {
3449 flag_u = true;
3450 } break;
3451
3452 case 'I': {
3453 flag_I = argv[argi] + 2;
3454 } break;
3455
3456 default:
3457 usage(argv[0]);
3458 return 1;
3459 break;
3460 }
3461
3462 _assert(flag_S || key.empty());
3463 _assert(flag_S || flag_I == NULL);
3464
3465 if (flag_d && !flag_h) {
3466 flag_h = true;
3467 fprintf(stderr, "WARNING: -d also (temporarily) does the behavior of -h for compatibility with a fork of ldid\n");
3468 }
3469
3470 if (files.empty())
3471 return 0;
3472
3473 size_t filei(0), filee(0);
3474 _foreach (file, files) try {
3475 std::string path(file);
3476
3477 struct stat info;
3478 if (stat(path.c_str(), &info) == -1) {
3479 fprintf(stderr, "ldid: %s: %s\n", path.c_str(), strerror(errno));
3480 exit(1);
3481 }
3482
3483 if (S_ISDIR(info.st_mode)) {
3484 if (!flag_S) {
3485 fprintf(stderr, "ldid: Only -S can be used on directories\n");
3486 exit(1);
3487 }
3488 #ifndef LDID_NOPLIST
3489 ldid::DiskFolder folder(path + "/");
3490 path += "/" + Sign("", folder, key, requirements, ldid::fun([&](const std::string &, const std::string &) -> std::string { return entitlements; }), dummy_).path;
3491 #else
3492 _assert(false);
3493 #endif
3494 } else if (flag_S || flag_r) {
3495 Map input(path, O_RDONLY, PROT_READ, MAP_PRIVATE);
3496
3497 std::filebuf output;
3498 Split split(path);
3499 auto temp(Temporary(output, split));
3500
3501 if (flag_r)
3502 ldid::Unsign(input.data(), input.size(), output, dummy_);
3503 else {
3504 std::string identifier(flag_I ?: split.base.c_str());
3505 ldid::Sign(input.data(), input.size(), output, identifier, entitlements, flag_M, requirements, key, slots, flags, platform, dummy_);
3506 }
3507
3508 Commit(path, temp);
3509 }
3510
3511 bool modify(false);
3512 #ifndef LDID_NOFLAGT
3513 if (flag_T)
3514 modify = true;
3515 #endif
3516 if (flag_s)
3517 modify = true;
3518
3519 Map mapping(path, modify);
3520 FatHeader fat_header(mapping.data(), mapping.size());
3521
3522 _foreach (mach_header, fat_header.GetMachHeaders()) {
3523 struct linkedit_data_command *signature(NULL);
3524 struct encryption_info_command *encryption(NULL);
3525
3526 if (flag_A) {
3527 if (mach_header.GetCPUType() != flag_CPUType)
3528 continue;
3529 if (mach_header.GetCPUSubtype() != flag_CPUSubtype)
3530 continue;
3531 }
3532
3533 if (flag_a)
3534 printf("cpu=0x%x:0x%x\n", mach_header.GetCPUType(), mach_header.GetCPUSubtype());
3535
3536 _foreach (load_command, mach_header.GetLoadCommands()) {
3537 uint32_t cmd(mach_header.Swap(load_command->cmd));
3538
3539 if (false);
3540 else if (cmd == LC_CODE_SIGNATURE)
3541 signature = reinterpret_cast<struct linkedit_data_command *>(load_command);
3542 else if (cmd == LC_ENCRYPTION_INFO || cmd == LC_ENCRYPTION_INFO_64)
3543 encryption = reinterpret_cast<struct encryption_info_command *>(load_command);
3544 else if (cmd == LC_LOAD_DYLIB) {
3545 volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(load_command));
3546 const char *name(reinterpret_cast<const char *>(load_command) + mach_header.Swap(dylib_command->dylib.name));
3547
3548 if (strcmp(name, "/System/Library/Frameworks/UIKit.framework/UIKit") == 0) {
3549 if (flag_u) {
3550 Version version;
3551 version.value = mach_header.Swap(dylib_command->dylib.current_version);
3552 printf("uikit=%u.%u.%u\n", version.major, version.minor, version.patch);
3553 }
3554 }
3555 }
3556 #ifndef LDID_NOFLAGT
3557 else if (cmd == LC_ID_DYLIB) {
3558 volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(load_command));
3559
3560 if (flag_T) {
3561 uint32_t timed;
3562
3563 if (!timeh)
3564 timed = timev;
3565 else {
3566 dylib_command->dylib.timestamp = 0;
3567 timed = hash(reinterpret_cast<uint8_t *>(mach_header.GetBase()), mach_header.GetSize(), timev);
3568 }
3569
3570 dylib_command->dylib.timestamp = mach_header.Swap(timed);
3571 }
3572 }
3573 #endif
3574 }
3575
3576 if (flag_d && encryption != NULL) {
3577 printf("cryptid=%d\n", mach_header.Swap(encryption->cryptid));
3578 }
3579
3580 if (flag_D) {
3581 _assert(encryption != NULL);
3582 encryption->cryptid = mach_header.Swap(0);
3583 }
3584
3585 if ((flag_e || flag_q || flag_s || flag_h) && signature == NULL) {
3586 fprintf(stderr, "ldid: -e, -q, -s, and -h requre a signed binary\n");
3587 exit(1);
3588 }
3589
3590 if (flag_e) {
3591 uint32_t data = mach_header.Swap(signature->dataoff);
3592
3593 uint8_t *top = reinterpret_cast<uint8_t *>(mach_header.GetBase());
3594 uint8_t *blob = top + data;
3595 struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
3596
3597 for (size_t index(0); index != Swap(super->count); ++index)
3598 if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) {
3599 uint32_t begin = Swap(super->index[index].offset);
3600 struct Blob *entitlements = reinterpret_cast<struct Blob *>(blob + begin);
3601 fwrite(entitlements + 1, 1, Swap(entitlements->length) - sizeof(*entitlements), stdout);
3602 }
3603 }
3604
3605 if (flag_q) {
3606 uint32_t data = mach_header.Swap(signature->dataoff);
3607
3608 uint8_t *top = reinterpret_cast<uint8_t *>(mach_header.GetBase());
3609 uint8_t *blob = top + data;
3610 struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
3611
3612 for (size_t index(0); index != Swap(super->count); ++index)
3613 if (Swap(super->index[index].type) == CSSLOT_REQUIREMENTS) {
3614 uint32_t begin = Swap(super->index[index].offset);
3615 struct Blob *requirement = reinterpret_cast<struct Blob *>(blob + begin);
3616 fwrite(requirement, 1, Swap(requirement->length), stdout);
3617 }
3618 }
3619
3620 if (flag_s) {
3621 uint32_t data = mach_header.Swap(signature->dataoff);
3622
3623 uint8_t *top = reinterpret_cast<uint8_t *>(mach_header.GetBase());
3624 uint8_t *blob = top + data;
3625 struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
3626
3627 for (size_t index(0); index != Swap(super->count); ++index)
3628 if (Swap(super->index[index].type) == CSSLOT_CODEDIRECTORY) {
3629 uint32_t begin = Swap(super->index[index].offset);
3630 struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin + sizeof(Blob));
3631
3632 uint8_t (*hashes)[LDID_SHA1_DIGEST_LENGTH] = reinterpret_cast<uint8_t (*)[LDID_SHA1_DIGEST_LENGTH]>(blob + begin + Swap(directory->hashOffset));
3633 uint32_t pages = Swap(directory->nCodeSlots);
3634
3635 if (pages != 1)
3636 for (size_t i = 0; i != pages - 1; ++i)
3637 LDID_SHA1(top + PageSize_ * i, PageSize_, hashes[i]);
3638 if (pages != 0)
3639 LDID_SHA1(top + PageSize_ * (pages - 1), ((data - 1) % PageSize_) + 1, hashes[pages - 1]);
3640 }
3641 }
3642
3643 if (flag_h) {
3644 auto algorithms(GetAlgorithms());
3645
3646 uint32_t data = mach_header.Swap(signature->dataoff);
3647
3648 uint8_t *top = reinterpret_cast<uint8_t *>(mach_header.GetBase());
3649 uint8_t *blob = top + data;
3650 struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
3651
3652 struct Candidate {
3653 CodeDirectory *directory_;
3654 size_t size_;
3655 Algorithm &algorithm_;
3656 std::string hash_;
3657 };
3658
3659 std::map<uint8_t, Candidate> candidates;
3660
3661 for (size_t index(0); index != Swap(super->count); ++index) {
3662 auto type(Swap(super->index[index].type));
3663 if ((type == CSSLOT_CODEDIRECTORY || type >= CSSLOT_ALTERNATE) && type != CSSLOT_SIGNATURESLOT) {
3664 uint32_t begin = Swap(super->index[index].offset);
3665 uint32_t end = index + 1 == Swap(super->count) ? Swap(super->blob.length) : Swap(super->index[index + 1].offset);
3666 struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin + sizeof(Blob));
3667 auto type(directory->hashType);
3668 _assert(type > 0 && type <= algorithms.size());
3669 auto &algorithm(*algorithms[type - 1]);
3670 uint8_t hash[algorithm.size_];
3671 algorithm(hash, blob + begin, end - begin);
3672 candidates.insert({type, {directory, end - begin, algorithm, Hex(hash, 20)}});
3673 }
3674 }
3675
3676 _assert(!candidates.empty());
3677 auto best(candidates.end());
3678 --best;
3679
3680 const auto directory(best->second.directory_);
3681 const auto flags(Swap(directory->flags));
3682
3683 std::string names;
3684 if (flags & kSecCodeSignatureHost)
3685 names += ",host";
3686 if (flags & kSecCodeSignatureAdhoc)
3687 names += ",adhoc";
3688 if (flags & kSecCodeSignatureForceHard)
3689 names += ",hard";
3690 if (flags & kSecCodeSignatureForceKill)
3691 names += ",kill";
3692 if (flags & kSecCodeSignatureForceExpiration)
3693 names += ",expires";
3694 if (flags & kSecCodeSignatureRestrict)
3695 names += ",restrict";
3696 if (flags & kSecCodeSignatureEnforcement)
3697 names += ",enforcement";
3698 if (flags & kSecCodeSignatureLibraryValidation)
3699 names += ",library-validation";
3700 if (flags & kSecCodeSignatureRuntime)
3701 names += ",runtime";
3702
3703 printf("CodeDirectory v=%x size=%zd flags=0x%x(%s) hashes=%d+%d location=embedded\n",
3704 Swap(directory->version), best->second.size_, flags, names.empty() ? "none" : names.c_str() + 1, Swap(directory->nCodeSlots), Swap(directory->nSpecialSlots));
3705 printf("Hash type=%s size=%d\n", best->second.algorithm_.name(), directory->hashSize);
3706
3707 std::string choices;
3708 for (const auto &candidate : candidates) {
3709 auto choice(candidate.second.algorithm_.name());
3710 choices += ',';
3711 choices += choice;
3712 printf("CandidateCDHash %s=%s\n", choice, candidate.second.hash_.c_str());
3713 }
3714 printf("Hash choices=%s\n", choices.c_str() + 1);
3715
3716 printf("CDHash=%s\n", best->second.hash_.c_str());
3717 }
3718 }
3719
3720 ++filei;
3721 } catch (const char *) {
3722 ++filee;
3723 ++filei;
3724 }
3725
3726 #ifndef LDID_NOSMIME
3727 # if OPENSSL_VERSION_MAJOR >= 3
3728 OSSL_PROVIDER_unload(legacy);
3729 OSSL_PROVIDER_unload(deflt);
3730 # endif
3731 #endif
3732
3733 return filee;
3734 }
3735 #endif