| ... | ... | @@ -11,75 +11,81 @@ pub fn rescanMac(cb: *Bundle, gpa: Allocator) RescanMacError!void { |
| 11 | 11 | cb.bytes.clearRetainingCapacity(); |
| 12 | 12 | cb.map.clearRetainingCapacity(); |
| 13 | 13 | |
| 14 | | const keychainPaths = [2][]const u8{ |
| 14 | const keychain_paths = [2][]const u8{ |
| 15 | 15 | "/System/Library/Keychains/SystemRootCertificates.keychain", |
| 16 | 16 | "/Library/Keychains/System.keychain", |
| 17 | 17 | }; |
| 18 | 18 | |
| 19 | | for (keychainPaths) |keychainPath| { |
| 20 | | const file = try fs.openFileAbsolute(keychainPath, .{}); |
| 21 | | defer file.close(); |
| 22 | | |
| 23 | | const bytes = try file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| 19 | for (keychain_paths) |keychain_path| { |
| 20 | const bytes = std.fs.cwd().readFileAlloc(keychain_path, gpa, .limited(std.math.maxInt(u32))) catch |err| switch (err) { |
| 21 | error.StreamTooLong => return error.FileTooBig, |
| 22 | else => |e| return e, |
| 23 | }; |
| 24 | 24 | defer gpa.free(bytes); |
| 25 | 25 | |
| 26 | 26 | var reader: std.Io.Reader = .fixed(bytes); |
| 27 | scanReader(cb, gpa, &reader) catch |err| switch (err) { |
| 28 | error.ReadFailed => unreachable, // prebuffered |
| 29 | else => |e| return e, |
| 30 | }; |
| 31 | } |
| 27 | 32 | |
| 28 | | const db_header = try reader.takeStruct(ApplDbHeader, .big); |
| 29 | | assert(mem.eql(u8, &db_header.signature, "kych")); |
| 33 | cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len); |
| 34 | } |
| 30 | 35 | |
| 31 | | reader.seek = db_header.schema_offset; |
| 36 | fn scanReader(cb: *Bundle, gpa: Allocator, reader: *std.Io.Reader) !void { |
| 37 | const db_header = try reader.takeStruct(ApplDbHeader, .big); |
| 38 | assert(mem.eql(u8, &db_header.signature, "kych")); |
| 32 | 39 | |
| 33 | | const db_schema = try reader.takeStruct(ApplDbSchema, .big); |
| 40 | reader.seek = db_header.schema_offset; |
| 34 | 41 | |
| 35 | | var table_list = try gpa.alloc(u32, db_schema.table_count); |
| 36 | | defer gpa.free(table_list); |
| 42 | const db_schema = try reader.takeStruct(ApplDbSchema, .big); |
| 37 | 43 | |
| 38 | | var table_idx: u32 = 0; |
| 39 | | while (table_idx < table_list.len) : (table_idx += 1) { |
| 40 | | table_list[table_idx] = try reader.takeInt(u32, .big); |
| 41 | | } |
| 44 | var table_list = try gpa.alloc(u32, db_schema.table_count); |
| 45 | defer gpa.free(table_list); |
| 42 | 46 | |
| 43 | | const now_sec = std.time.timestamp(); |
| 47 | var table_idx: u32 = 0; |
| 48 | while (table_idx < table_list.len) : (table_idx += 1) { |
| 49 | table_list[table_idx] = try reader.takeInt(u32, .big); |
| 50 | } |
| 44 | 51 | |
| 45 | | for (table_list) |table_offset| { |
| 46 | | reader.seek = db_header.schema_offset + table_offset; |
| 52 | const now_sec = std.time.timestamp(); |
| 47 | 53 | |
| 48 | | const table_header = try reader.takeStruct(TableHeader, .big); |
| 54 | for (table_list) |table_offset| { |
| 55 | reader.seek = db_header.schema_offset + table_offset; |
| 49 | 56 | |
| 50 | | if (@as(std.c.DB_RECORDTYPE, @enumFromInt(table_header.table_id)) != .X509_CERTIFICATE) { |
| 51 | | continue; |
| 52 | | } |
| 57 | const table_header = try reader.takeStruct(TableHeader, .big); |
| 53 | 58 | |
| 54 | | var record_list = try gpa.alloc(u32, table_header.record_count); |
| 55 | | defer gpa.free(record_list); |
| 59 | if (@as(std.c.DB_RECORDTYPE, @enumFromInt(table_header.table_id)) != .X509_CERTIFICATE) { |
| 60 | continue; |
| 61 | } |
| 62 | |
| 63 | var record_list = try gpa.alloc(u32, table_header.record_count); |
| 64 | defer gpa.free(record_list); |
| 56 | 65 | |
| 57 | | var record_idx: u32 = 0; |
| 58 | | while (record_idx < record_list.len) : (record_idx += 1) { |
| 59 | | record_list[record_idx] = try reader.takeInt(u32, .big); |
| 60 | | } |
| 66 | var record_idx: u32 = 0; |
| 67 | while (record_idx < record_list.len) : (record_idx += 1) { |
| 68 | record_list[record_idx] = try reader.takeInt(u32, .big); |
| 69 | } |
| 61 | 70 | |
| 62 | | for (record_list) |record_offset| { |
| 63 | | // An offset of zero means that the record is not present. |
| 64 | | // An offset that is not 4-byte-aligned is invalid. |
| 65 | | if (record_offset == 0 or record_offset % 4 != 0) continue; |
| 71 | for (record_list) |record_offset| { |
| 72 | // An offset of zero means that the record is not present. |
| 73 | // An offset that is not 4-byte-aligned is invalid. |
| 74 | if (record_offset == 0 or record_offset % 4 != 0) continue; |
| 66 | 75 | |
| 67 | | reader.seek = db_header.schema_offset + table_offset + record_offset; |
| 76 | reader.seek = db_header.schema_offset + table_offset + record_offset; |
| 68 | 77 | |
| 69 | | const cert_header = try reader.takeStruct(X509CertHeader, .big); |
| 78 | const cert_header = try reader.takeStruct(X509CertHeader, .big); |
| 70 | 79 | |
| 71 | | if (cert_header.cert_size == 0) continue; |
| 80 | if (cert_header.cert_size == 0) continue; |
| 72 | 81 | |
| 73 | | const cert_start = @as(u32, @intCast(cb.bytes.items.len)); |
| 74 | | const dest_buf = try cb.bytes.addManyAsSlice(gpa, cert_header.cert_size); |
| 75 | | try reader.readSliceAll(dest_buf); |
| 82 | const cert_start: u32 = @intCast(cb.bytes.items.len); |
| 83 | const dest_buf = try cb.bytes.addManyAsSlice(gpa, cert_header.cert_size); |
| 84 | try reader.readSliceAll(dest_buf); |
| 76 | 85 | |
| 77 | | try cb.parseCert(gpa, cert_start, now_sec); |
| 78 | | } |
| 86 | try cb.parseCert(gpa, cert_start, now_sec); |
| 79 | 87 | } |
| 80 | 88 | } |
| 81 | | |
| 82 | | cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len); |
| 83 | 89 | } |
| 84 | 90 | |
| 85 | 91 | const ApplDbHeader = extern struct { |