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