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