endpoint_security 0.1.0-universal-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +44 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +114 -0
  5. data/Rakefile +170 -0
  6. data/codegen/ast.rb +67 -0
  7. data/codegen/emit_c.rb +68 -0
  8. data/codegen/emit_ruby.rb +128 -0
  9. data/codegen/ir.rb +193 -0
  10. data/codegen/overlay/availability.yml +158 -0
  11. data/codegen/overlay/version_map.yml +92 -0
  12. data/codegen/run.rb +80 -0
  13. data/codegen/snapshots/26.5.json +5828 -0
  14. data/examples/eslogger_clone.rb +5 -0
  15. data/examples/execblock.rb +16 -0
  16. data/examples/filemon.rb +10 -0
  17. data/examples/procmon.rb +13 -0
  18. data/ext/endpoint_security/client.c +795 -0
  19. data/ext/endpoint_security/client.h +88 -0
  20. data/ext/endpoint_security/endpoint_security.c +17 -0
  21. data/ext/endpoint_security/extconf.rb +37 -0
  22. data/ext/endpoint_security/field.c +719 -0
  23. data/ext/endpoint_security/field.h +42 -0
  24. data/ext/endpoint_security/generated/es_schema.c +1233 -0
  25. data/ext/endpoint_security/message.c +426 -0
  26. data/ext/endpoint_security/message.h +13 -0
  27. data/ext/endpoint_security/mute.c +291 -0
  28. data/ext/endpoint_security/mute.h +8 -0
  29. data/ext/endpoint_security/queue.c +124 -0
  30. data/ext/endpoint_security/queue.h +46 -0
  31. data/ext/endpoint_security/watchdog.c +243 -0
  32. data/lib/endpoint_security/availability.rb +30 -0
  33. data/lib/endpoint_security/client.rb +280 -0
  34. data/lib/endpoint_security/diagnostics.rb +26 -0
  35. data/lib/endpoint_security/doctor.rb +35 -0
  36. data/lib/endpoint_security/errors.rb +42 -0
  37. data/lib/endpoint_security/generated/availability.rb +169 -0
  38. data/lib/endpoint_security/generated/enums.rb +409 -0
  39. data/lib/endpoint_security/generated/event_types.rb +520 -0
  40. data/lib/endpoint_security/message.rb +29 -0
  41. data/lib/endpoint_security/object_model.rb +170 -0
  42. data/lib/endpoint_security/recorder.rb +26 -0
  43. data/lib/endpoint_security/version.rb +6 -0
  44. data/lib/endpoint_security.rb +38 -0
  45. data/support/entitlements.plist +8 -0
  46. data/support/esmock/esmock.c +482 -0
  47. data/support/esmock/esmock.h +21 -0
  48. data/support/sign_ruby.sh +13 -0
  49. metadata +90 -0
@@ -0,0 +1,719 @@
1
+ /* field.c
2
+ * Calling thread: Ruby dispatcher with the GVL. Every view retains its owning Message.
3
+ */
4
+ #include "field.h"
5
+
6
+ #include <bsm/libbsm.h>
7
+ #include <limits.h>
8
+ #include <ruby/encoding.h>
9
+ #include <string.h>
10
+ #include <sys/acl.h>
11
+ #include <sys/attr.h>
12
+ #include <sys/mount.h>
13
+ #include <sys/proc_info.h>
14
+
15
+ #include "message.h"
16
+
17
+ typedef struct {
18
+ VALUE owner;
19
+ const unsigned char *pointer;
20
+ const esrb_schema_t *schema;
21
+ uint32_t message_version;
22
+ bool strict_version;
23
+ } esrb_view_t;
24
+
25
+ static VALUE c_native_view;
26
+
27
+ static const esrb_schema_t *
28
+ find_schema(const char *name)
29
+ {
30
+ for (size_t index = 0; index < esrb_schema_count; index++) {
31
+ if (strcmp(esrb_schemas[index].name, name) == 0) {
32
+ return &esrb_schemas[index];
33
+ }
34
+ }
35
+ return NULL;
36
+ }
37
+
38
+ static void
39
+ view_mark(void *pointer)
40
+ {
41
+ esrb_view_t *view = pointer;
42
+ rb_gc_mark(view->owner);
43
+ }
44
+
45
+ static void
46
+ view_free(void *pointer)
47
+ {
48
+ xfree(pointer);
49
+ }
50
+
51
+ static size_t
52
+ view_size(const void *pointer)
53
+ {
54
+ return pointer == NULL ? 0 : sizeof(esrb_view_t);
55
+ }
56
+
57
+ static const rb_data_type_t view_type = {
58
+ .wrap_struct_name = "EndpointSecurity::NativeView",
59
+ .function = {.dmark = view_mark, .dfree = view_free, .dsize = view_size},
60
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
61
+ };
62
+
63
+ static esrb_view_t *
64
+ get_view(VALUE self)
65
+ {
66
+ esrb_view_t *view;
67
+ TypedData_Get_Struct(self, esrb_view_t, &view_type, view);
68
+ if (!esrb_message_valid_object(view->owner)) {
69
+ rb_raise(rb_path2class("EndpointSecurity::MessageInvalidatedError"), "message has been released");
70
+ }
71
+ return view;
72
+ }
73
+
74
+ static VALUE
75
+ class_for_schema(const char *schema)
76
+ {
77
+ if (strcmp(schema, "es_process_t") == 0) {
78
+ return rb_path2class("EndpointSecurity::Process");
79
+ }
80
+ if (strcmp(schema, "es_file_t") == 0) {
81
+ return rb_path2class("EndpointSecurity::File");
82
+ }
83
+ if (strcmp(schema, "es_thread_t") == 0) {
84
+ return rb_path2class("EndpointSecurity::Thread");
85
+ }
86
+ return strncmp(schema, "es_event_", 9) == 0 ? rb_path2class("EndpointSecurity::Event") : c_native_view;
87
+ }
88
+
89
+ VALUE
90
+ esrb_view_wrap(VALUE owner, const void *pointer, const char *schema_name, uint32_t message_version, bool strict_version)
91
+ {
92
+ if (pointer == NULL) {
93
+ return Qnil;
94
+ }
95
+ const esrb_schema_t *schema = find_schema(schema_name);
96
+ if (schema == NULL) {
97
+ return Qnil;
98
+ }
99
+
100
+ esrb_view_t *view;
101
+ VALUE object = TypedData_Make_Struct(class_for_schema(schema_name), esrb_view_t, &view_type, view);
102
+ view->owner = owner;
103
+ view->pointer = pointer;
104
+ view->schema = schema;
105
+ view->message_version = message_version;
106
+ view->strict_version = strict_version;
107
+ return object;
108
+ }
109
+
110
+ static VALUE
111
+ time_value(time_t seconds, long nanoseconds)
112
+ {
113
+ return rb_time_nano_new(seconds, nanoseconds);
114
+ }
115
+
116
+ VALUE
117
+ esrb_string_token_value(const es_string_token_t *token)
118
+ {
119
+ if (token->length > LONG_MAX) {
120
+ return Qnil;
121
+ }
122
+ VALUE string = token->data == NULL ? rb_str_new("", 0) : rb_str_new(token->data, (long)token->length);
123
+ VALUE mode = rb_funcall(rb_path2class("EndpointSecurity"), rb_intern("string_encoding"), 0);
124
+ rb_enc_associate(string, mode == ID2SYM(rb_intern("binary")) ? rb_ascii8bit_encoding() : rb_utf8_encoding());
125
+ return string;
126
+ }
127
+
128
+ static VALUE
129
+ tagged_union_value(const char *kind, VALUE value)
130
+ {
131
+ return rb_funcall(rb_path2class("EndpointSecurity::TaggedUnion"), rb_intern("__native_new"), 2,
132
+ ID2SYM(rb_intern(kind)), value);
133
+ }
134
+
135
+ static VALUE
136
+ hex_value(const uint8_t *bytes, size_t length)
137
+ {
138
+ static const char digits[] = "0123456789abcdef";
139
+ VALUE string = rb_str_new(NULL, (long)(length * 2));
140
+ char *output = RSTRING_PTR(string);
141
+ for (size_t index = 0; index < length; index++) {
142
+ output[index * 2] = digits[bytes[index] >> 4];
143
+ output[index * 2 + 1] = digits[bytes[index] & 0x0f];
144
+ }
145
+ return string;
146
+ }
147
+
148
+ static VALUE
149
+ binary_value(const void *bytes, size_t length)
150
+ {
151
+ VALUE value = rb_str_new(bytes, (long)length);
152
+ rb_enc_associate(value, rb_ascii8bit_encoding());
153
+ return value;
154
+ }
155
+
156
+ static VALUE
157
+ acl_value(acl_t acl)
158
+ {
159
+ if (acl == NULL) {
160
+ return Qnil;
161
+ }
162
+ ssize_t size = acl_size(acl);
163
+ if (size <= 0) {
164
+ return Qnil;
165
+ }
166
+ VALUE value = rb_str_new(NULL, size);
167
+ ssize_t copied = acl_copy_ext(RSTRING_PTR(value), acl, size);
168
+ if (copied < 0) {
169
+ return Qnil;
170
+ }
171
+ rb_str_set_len(value, copied);
172
+ rb_enc_associate(value, rb_ascii8bit_encoding());
173
+ return value;
174
+ }
175
+
176
+ static VALUE
177
+ uid_union(bool available, uid_t uid)
178
+ {
179
+ return tagged_union_value(available ? "uid" : "unavailable", available ? UINT2NUM(uid) : Qnil);
180
+ }
181
+
182
+ static VALUE
183
+ destination_union(const esrb_view_t *view, bool create)
184
+ {
185
+ es_destination_type_t type;
186
+ es_file_t *existing_file;
187
+ es_file_t *directory;
188
+ es_string_token_t filename;
189
+ mode_t mode = 0;
190
+ if (create) {
191
+ const es_event_create_t *event = (const es_event_create_t *)view->pointer;
192
+ type = event->destination_type;
193
+ existing_file = event->destination.existing_file;
194
+ directory = event->destination.new_path.dir;
195
+ filename = event->destination.new_path.filename;
196
+ mode = event->destination.new_path.mode;
197
+ } else {
198
+ const es_event_rename_t *event = (const es_event_rename_t *)view->pointer;
199
+ type = event->destination_type;
200
+ existing_file = event->destination.existing_file;
201
+ directory = event->destination.new_path.dir;
202
+ filename = event->destination.new_path.filename;
203
+ }
204
+ if (type == ES_DESTINATION_TYPE_EXISTING_FILE) {
205
+ VALUE file = esrb_view_wrap(
206
+ view->owner, existing_file, "es_file_t", view->message_version, view->strict_version);
207
+ return tagged_union_value("existing_file", file);
208
+ }
209
+ if (type != ES_DESTINATION_TYPE_NEW_PATH) {
210
+ return tagged_union_value("unknown", Qnil);
211
+ }
212
+
213
+ VALUE path = rb_hash_new();
214
+ rb_hash_aset(path, ID2SYM(rb_intern("dir")),
215
+ esrb_view_wrap(view->owner, directory, "es_file_t", view->message_version, view->strict_version));
216
+ rb_hash_aset(path, ID2SYM(rb_intern("filename")), esrb_string_token_value(&filename));
217
+ if (create) {
218
+ rb_hash_aset(path, ID2SYM(rb_intern("mode")), UINT2NUM(mode));
219
+ }
220
+ return tagged_union_value("new_path", path);
221
+ }
222
+
223
+ static VALUE
224
+ authentication_union(const esrb_view_t *view)
225
+ {
226
+ const es_event_authentication_t *event = (const es_event_authentication_t *)view->pointer;
227
+ const void *pointer = NULL;
228
+ const char *kind = "unknown";
229
+ const char *schema = NULL;
230
+ switch (event->type) {
231
+ case ES_AUTHENTICATION_TYPE_OD:
232
+ kind = "od";
233
+ schema = "es_event_authentication_od_t";
234
+ pointer = event->data.od;
235
+ break;
236
+ case ES_AUTHENTICATION_TYPE_TOUCHID:
237
+ kind = "touchid";
238
+ schema = "es_event_authentication_touchid_t";
239
+ pointer = event->data.touchid;
240
+ break;
241
+ case ES_AUTHENTICATION_TYPE_TOKEN:
242
+ kind = "token";
243
+ schema = "es_event_authentication_token_t";
244
+ pointer = event->data.token;
245
+ break;
246
+ case ES_AUTHENTICATION_TYPE_AUTO_UNLOCK:
247
+ kind = "auto_unlock";
248
+ schema = "es_event_authentication_auto_unlock_t";
249
+ pointer = event->data.auto_unlock;
250
+ break;
251
+ default:
252
+ break;
253
+ }
254
+ VALUE value = schema == NULL
255
+ ? Qnil
256
+ : esrb_view_wrap(view->owner, pointer, schema, view->message_version, view->strict_version);
257
+ return tagged_union_value(kind, value);
258
+ }
259
+
260
+ static VALUE
261
+ od_member_union(const esrb_view_t *view)
262
+ {
263
+ const es_od_member_id_t *member = (const es_od_member_id_t *)view->pointer;
264
+ if (member->member_type == ES_OD_MEMBER_TYPE_USER_NAME) {
265
+ return tagged_union_value("name", esrb_string_token_value(&member->member_value.name));
266
+ }
267
+ if (member->member_type != ES_OD_MEMBER_TYPE_USER_UUID && member->member_type != ES_OD_MEMBER_TYPE_GROUP_UUID) {
268
+ return tagged_union_value("unknown", Qnil);
269
+ }
270
+ return tagged_union_value("uuid", hex_value(member->member_value.uuid, sizeof(uuid_t)));
271
+ }
272
+
273
+ static VALUE
274
+ od_member_array_union(const esrb_view_t *view)
275
+ {
276
+ const es_od_member_id_array_t *members = (const es_od_member_id_array_t *)view->pointer;
277
+ if (members->member_count > UINT32_MAX) {
278
+ return tagged_union_value("invalid", Qnil);
279
+ }
280
+ VALUE values = rb_ary_new_capa((long)members->member_count);
281
+ if (members->member_type == ES_OD_MEMBER_TYPE_USER_NAME) {
282
+ for (size_t index = 0; index < members->member_count; index++) {
283
+ rb_ary_push(values, esrb_string_token_value(&members->member_array.names[index]));
284
+ }
285
+ return tagged_union_value("names", values);
286
+ }
287
+ if (members->member_type != ES_OD_MEMBER_TYPE_USER_UUID && members->member_type != ES_OD_MEMBER_TYPE_GROUP_UUID) {
288
+ return tagged_union_value("unknown", Qnil);
289
+ }
290
+ for (size_t index = 0; index < members->member_count; index++) {
291
+ rb_ary_push(values, hex_value(members->member_array.uuids[index], sizeof(uuid_t)));
292
+ }
293
+ return tagged_union_value("uuids", values);
294
+ }
295
+
296
+ static VALUE
297
+ read_union(const esrb_view_t *view, const esrb_field_t *field)
298
+ {
299
+ const char *schema = view->schema->name;
300
+ if (strcmp(field->name, "destination") == 0 && strcmp(schema, "es_event_rename_t") == 0) {
301
+ return destination_union(view, false);
302
+ }
303
+ if (strcmp(field->name, "destination") == 0 && strcmp(schema, "es_event_create_t") == 0) {
304
+ return destination_union(view, true);
305
+ }
306
+ if (strcmp(field->name, "uid") == 0 && strcmp(schema, "es_event_authentication_touchid_t") == 0) {
307
+ const es_event_authentication_touchid_t *event = (const es_event_authentication_touchid_t *)view->pointer;
308
+ return uid_union(event->has_uid, event->uid.uid);
309
+ }
310
+ if (strcmp(field->name, "uid") == 0 && strcmp(schema, "es_event_openssh_login_t") == 0) {
311
+ const es_event_openssh_login_t *event = (const es_event_openssh_login_t *)view->pointer;
312
+ return uid_union(event->has_uid, event->uid.uid);
313
+ }
314
+ if (strcmp(field->name, "uid") == 0 && strcmp(schema, "es_event_login_login_t") == 0) {
315
+ const es_event_login_login_t *event = (const es_event_login_login_t *)view->pointer;
316
+ return uid_union(event->has_uid, event->uid.uid);
317
+ }
318
+ if (strcmp(field->name, "to_uid") == 0 && strcmp(schema, "es_event_su_t") == 0) {
319
+ const es_event_su_t *event = (const es_event_su_t *)view->pointer;
320
+ return uid_union(event->has_to_uid, event->to_uid.uid);
321
+ }
322
+ if (strcmp(schema, "es_event_sudo_t") == 0 && strcmp(field->name, "from_uid") == 0) {
323
+ const es_event_sudo_t *event = (const es_event_sudo_t *)view->pointer;
324
+ return uid_union(event->has_from_uid, event->from_uid.uid);
325
+ }
326
+ if (strcmp(schema, "es_event_sudo_t") == 0 && strcmp(field->name, "to_uid") == 0) {
327
+ const es_event_sudo_t *event = (const es_event_sudo_t *)view->pointer;
328
+ return uid_union(event->has_to_uid, event->to_uid.uid);
329
+ }
330
+ if (strcmp(field->name, "data") == 0 && strcmp(schema, "es_event_authentication_t") == 0) {
331
+ return authentication_union(view);
332
+ }
333
+ if (strcmp(field->name, "member_value") == 0 && strcmp(schema, "es_od_member_id_t") == 0) {
334
+ return od_member_union(view);
335
+ }
336
+ if (strcmp(field->name, "member_array") == 0 && strcmp(schema, "es_od_member_id_array_t") == 0) {
337
+ return od_member_array_union(view);
338
+ }
339
+ if (strcmp(field->name, "file") == 0 && strcmp(schema, "es_event_gatekeeper_user_override_t") == 0) {
340
+ const es_event_gatekeeper_user_override_t *event = (const es_event_gatekeeper_user_override_t *)view->pointer;
341
+ if (event->file_type == ES_GATEKEEPER_USER_OVERRIDE_FILE_TYPE_PATH) {
342
+ return tagged_union_value("path", esrb_string_token_value(&event->file.file_path));
343
+ }
344
+ if (event->file_type != ES_GATEKEEPER_USER_OVERRIDE_FILE_TYPE_FILE) {
345
+ return tagged_union_value("unknown", Qnil);
346
+ }
347
+ VALUE file = esrb_view_wrap(
348
+ view->owner, event->file.file, "es_file_t", view->message_version, view->strict_version);
349
+ return tagged_union_value("file", file);
350
+ }
351
+ if (strcmp(field->name, "acl") == 0 && strcmp(schema, "es_event_setacl_t") == 0) {
352
+ const es_event_setacl_t *event = (const es_event_setacl_t *)view->pointer;
353
+ if (event->set_or_clear == ES_SET) {
354
+ return tagged_union_value("set", acl_value(event->acl.set));
355
+ }
356
+ return tagged_union_value(event->set_or_clear == ES_CLEAR ? "clear" : "unknown", Qnil);
357
+ }
358
+ return Qundef;
359
+ }
360
+
361
+ static VALUE
362
+ string_array(const es_string_token_t *tokens, size_t count)
363
+ {
364
+ if (tokens == NULL || count > UINT32_MAX) {
365
+ return rb_ary_new();
366
+ }
367
+ VALUE values = rb_ary_new_capa((long)count);
368
+ for (size_t index = 0; index < count; index++) {
369
+ rb_ary_push(values, esrb_string_token_value(&tokens[index]));
370
+ }
371
+ return values;
372
+ }
373
+
374
+ static VALUE
375
+ read_array(const esrb_view_t *view, const esrb_field_t *field)
376
+ {
377
+ const char *schema = view->schema->name;
378
+ if (strcmp(schema, "es_event_su_t") == 0) {
379
+ const es_event_su_t *event = (const es_event_su_t *)view->pointer;
380
+ if (strcmp(field->name, "argv") == 0) {
381
+ return string_array(event->argv, event->argc);
382
+ }
383
+ if (strcmp(field->name, "env") == 0) {
384
+ return string_array(event->env, event->env_count);
385
+ }
386
+ }
387
+ if (strcmp(schema, "es_event_authorization_petition_t") == 0 && strcmp(field->name, "rights") == 0) {
388
+ const es_event_authorization_petition_t *event = (const es_event_authorization_petition_t *)view->pointer;
389
+ return string_array(event->rights, event->right_count);
390
+ }
391
+ if (strcmp(schema, "es_event_od_attribute_set_t") == 0 && strcmp(field->name, "attribute_values") == 0) {
392
+ const es_event_od_attribute_set_t *event = (const es_event_od_attribute_set_t *)view->pointer;
393
+ return string_array(event->attribute_values, event->attribute_value_count);
394
+ }
395
+ if (strcmp(schema, "es_event_authorization_judgement_t") == 0 && strcmp(field->name, "results") == 0) {
396
+ const es_event_authorization_judgement_t *event = (const es_event_authorization_judgement_t *)view->pointer;
397
+ if (event->results == NULL || event->result_count > UINT32_MAX) {
398
+ return rb_ary_new();
399
+ }
400
+ VALUE values = rb_ary_new_capa((long)event->result_count);
401
+ for (size_t index = 0; index < event->result_count; index++) {
402
+ rb_ary_push(values, esrb_view_wrap(view->owner, &event->results[index], "es_authorization_result_t",
403
+ view->message_version, view->strict_version));
404
+ }
405
+ return values;
406
+ }
407
+ return Qundef;
408
+ }
409
+
410
+ static VALUE
411
+ audit_token_value(const audit_token_t *token)
412
+ {
413
+ VALUE arguments[] = {
414
+ UINT2NUM(audit_token_to_pid(*token)),
415
+ UINT2NUM(audit_token_to_pidversion(*token)),
416
+ UINT2NUM(audit_token_to_ruid(*token)),
417
+ UINT2NUM(audit_token_to_euid(*token)),
418
+ UINT2NUM(audit_token_to_rgid(*token)),
419
+ UINT2NUM(audit_token_to_egid(*token)),
420
+ UINT2NUM(audit_token_to_asid(*token)),
421
+ UINT2NUM(audit_token_to_auid(*token))
422
+ };
423
+ return rb_funcallv(rb_path2class("EndpointSecurity::AuditToken"), rb_intern("__native_new"), 8, arguments);
424
+ }
425
+
426
+ static VALUE
427
+ stat_value(const struct stat *stat)
428
+ {
429
+ VALUE arguments[] = {
430
+ ULL2NUM(stat->st_dev), ULL2NUM(stat->st_ino), UINT2NUM(stat->st_mode), UINT2NUM(stat->st_nlink),
431
+ UINT2NUM(stat->st_uid), UINT2NUM(stat->st_gid), ULL2NUM(stat->st_rdev), LL2NUM(stat->st_size),
432
+ LL2NUM(stat->st_blocks), LONG2NUM(stat->st_blksize), time_value(stat->st_atimespec.tv_sec, stat->st_atimespec.tv_nsec),
433
+ time_value(stat->st_mtimespec.tv_sec, stat->st_mtimespec.tv_nsec),
434
+ time_value(stat->st_ctimespec.tv_sec, stat->st_ctimespec.tv_nsec),
435
+ time_value(stat->st_birthtimespec.tv_sec, stat->st_birthtimespec.tv_nsec)
436
+ };
437
+ return rb_funcallv(rb_path2class("EndpointSecurity::Stat"), rb_intern("__native_new"), 14, arguments);
438
+ }
439
+
440
+ static VALUE
441
+ statfs_value(const struct statfs *statfs)
442
+ {
443
+ size_t type_length = strnlen(statfs->f_fstypename, sizeof(statfs->f_fstypename));
444
+ size_t path_length = strnlen(statfs->f_mntonname, sizeof(statfs->f_mntonname));
445
+ size_t source_length = strnlen(statfs->f_mntfromname, sizeof(statfs->f_mntfromname));
446
+ VALUE fsid = rb_ary_new_from_args(2, INT2NUM(statfs->f_fsid.val[0]), INT2NUM(statfs->f_fsid.val[1]));
447
+ VALUE arguments[] = {
448
+ UINT2NUM(statfs->f_bsize), INT2NUM(statfs->f_iosize), ULL2NUM(statfs->f_blocks), ULL2NUM(statfs->f_bfree),
449
+ ULL2NUM(statfs->f_bavail), ULL2NUM(statfs->f_files), ULL2NUM(statfs->f_ffree), fsid,
450
+ UINT2NUM(statfs->f_owner), UINT2NUM(statfs->f_type), UINT2NUM(statfs->f_flags), UINT2NUM(statfs->f_fssubtype),
451
+ rb_str_new(statfs->f_fstypename, (long)type_length), rb_str_new(statfs->f_mntonname, (long)path_length),
452
+ rb_str_new(statfs->f_mntfromname, (long)source_length), UINT2NUM(statfs->f_flags_ext)
453
+ };
454
+ return rb_funcallv(rb_path2class("EndpointSecurity::Statfs"), rb_intern("__native_new"), 16, arguments);
455
+ }
456
+
457
+ static VALUE
458
+ attrlist_value(const struct attrlist *attributes)
459
+ {
460
+ VALUE arguments[] = {
461
+ UINT2NUM(attributes->bitmapcount), UINT2NUM(attributes->reserved), UINT2NUM(attributes->commonattr),
462
+ UINT2NUM(attributes->volattr), UINT2NUM(attributes->dirattr), UINT2NUM(attributes->fileattr),
463
+ UINT2NUM(attributes->forkattr)
464
+ };
465
+ return rb_funcallv(rb_path2class("EndpointSecurity::Attrlist"), rb_intern("__native_new"), 7, arguments);
466
+ }
467
+
468
+ static const esrb_field_t *
469
+ find_field(const esrb_view_t *view, const char *name)
470
+ {
471
+ for (size_t index = 0; index < view->schema->field_count; index++) {
472
+ if (strcmp(view->schema->fields[index].name, name) == 0) {
473
+ return &view->schema->fields[index];
474
+ }
475
+ }
476
+ return NULL;
477
+ }
478
+
479
+ static void
480
+ referenced_schema_name(const char *type, char *name, size_t capacity)
481
+ {
482
+ if (strncmp(type, "union ", 6) == 0 || strncmp(type, "struct ", 7) == 0) {
483
+ name[0] = '\0';
484
+ return;
485
+ }
486
+ const char *start = strstr(type, "es_");
487
+ const char *end = start == NULL ? NULL : strstr(start, "_t");
488
+ if (end == NULL || (size_t)(end + 2 - start) >= capacity) {
489
+ name[0] = '\0';
490
+ return;
491
+ }
492
+ size_t length = (size_t)(end + 2 - start);
493
+ memcpy(name, start, length);
494
+ name[length] = '\0';
495
+ }
496
+
497
+ static VALUE
498
+ read_field(VALUE self, VALUE name_value)
499
+ {
500
+ esrb_view_t *view = get_view(self);
501
+ const char *name = StringValueCStr(name_value);
502
+ const esrb_field_t *field = find_field(view, name);
503
+ if (field == NULL) {
504
+ return Qundef;
505
+ }
506
+ if (field->minimum_version > view->message_version) {
507
+ if (view->strict_version) {
508
+ rb_raise(rb_path2class("EndpointSecurity::FieldUnavailableError"),
509
+ "%s requires message version %u (got %u)", field->name, field->minimum_version, view->message_version);
510
+ }
511
+ return Qnil;
512
+ }
513
+
514
+ const unsigned char *address = view->pointer + field->offset;
515
+ const char *type = field->type;
516
+ if (strncmp(type, "union ", 6) == 0) {
517
+ VALUE value = read_union(view, field);
518
+ return value == Qundef ? Qnil : value;
519
+ }
520
+ if (strchr(type, '*') != NULL) {
521
+ VALUE value = read_array(view, field);
522
+ if (value != Qundef) {
523
+ return value;
524
+ }
525
+ }
526
+ if (strcmp(type, "es_string_token_t") == 0) {
527
+ return esrb_string_token_value((const es_string_token_t *)address);
528
+ }
529
+ if (strcmp(type, "es_string_token_t *") == 0) {
530
+ const es_string_token_t *token = *(const es_string_token_t *const *)address;
531
+ return token == NULL ? Qnil : esrb_string_token_value(token);
532
+ }
533
+ if (strcmp(type, "es_token_t") == 0) {
534
+ const es_token_t *token = (const es_token_t *)address;
535
+ if (token->size > LONG_MAX) {
536
+ return Qnil;
537
+ }
538
+ return token->data == NULL ? rb_str_new("", 0) : rb_str_new((const char *)token->data, (long)token->size);
539
+ }
540
+ if (strcmp(type, "audit_token_t") == 0) {
541
+ return audit_token_value((const audit_token_t *)address);
542
+ }
543
+ if (strcmp(type, "audit_token_t *") == 0) {
544
+ const audit_token_t *token = *(const audit_token_t *const *)address;
545
+ return token == NULL ? Qnil : audit_token_value(token);
546
+ }
547
+ if (strcmp(type, "struct stat") == 0) {
548
+ return stat_value((const struct stat *)address);
549
+ }
550
+ if (strcmp(type, "struct statfs *") == 0) {
551
+ const struct statfs *statfs = *(const struct statfs *const *)address;
552
+ return statfs == NULL ? Qnil : statfs_value(statfs);
553
+ }
554
+ if (strcmp(type, "struct attrlist") == 0) {
555
+ return attrlist_value((const struct attrlist *)address);
556
+ }
557
+ if (strcmp(type, "struct timespec") == 0) {
558
+ const struct timespec *time = (const struct timespec *)address;
559
+ return time_value(time->tv_sec, time->tv_nsec);
560
+ }
561
+ if (strcmp(type, "struct timeval") == 0) {
562
+ const struct timeval *time = (const struct timeval *)address;
563
+ return time_value(time->tv_sec, time->tv_usec * 1000L);
564
+ }
565
+ if (strcmp(type, "bool") == 0 || strcmp(type, "_Bool") == 0) {
566
+ return *(const bool *)address ? Qtrue : Qfalse;
567
+ }
568
+ if (strstr(type, "[20]") != NULL) {
569
+ return binary_value(address, 20);
570
+ }
571
+ if (strcmp(type, "es_sha256_t *") == 0) {
572
+ const es_sha256_t *sha256 = *(const es_sha256_t *const *)address;
573
+ return sha256 == NULL ? Qnil : binary_value(*sha256, sizeof(*sha256));
574
+ }
575
+ if (strcmp(type, "struct _acl *") == 0) {
576
+ return acl_value(*(acl_t const *)address);
577
+ }
578
+ if (strcmp(view->schema->name, "es_fd_t") == 0 && strcmp(field->name, "pipe") == 0) {
579
+ const es_fd_t *descriptor = (const es_fd_t *)view->pointer;
580
+ if (descriptor->fdtype != PROX_FDTYPE_PIPE) {
581
+ return Qnil;
582
+ }
583
+ VALUE pipe = rb_hash_new();
584
+ rb_hash_aset(pipe, ID2SYM(rb_intern("pipe_id")), ULL2NUM(descriptor->pipe.pipe_id));
585
+ return pipe;
586
+ }
587
+
588
+ char schema_name[128];
589
+ referenced_schema_name(type, schema_name, sizeof(schema_name));
590
+ if (schema_name[0] != '\0' && find_schema(schema_name) != NULL) {
591
+ const void *nested = strchr(type, '*') == NULL ? address : *(const void *const *)address;
592
+ return esrb_view_wrap(view->owner, nested, schema_name, view->message_version, view->strict_version);
593
+ }
594
+ if (strchr(type, '*') != NULL) {
595
+ return Qnil;
596
+ }
597
+ if (strncmp(type, "es_", 3) == 0) {
598
+ VALUE enumeration = rb_path2class("EndpointSecurity::Enum");
599
+ return rb_funcall(enumeration, rb_intern("symbol"), 2, rb_str_new_cstr(type), INT2NUM(*(const int32_t *)address));
600
+ }
601
+ if (strcmp(type, "unsigned long long") == 0 || strcmp(type, "uint64_t") == 0) {
602
+ return ULL2NUM(*(const uint64_t *)address);
603
+ }
604
+ if (strcmp(type, "long long") == 0 || strcmp(type, "int64_t") == 0) {
605
+ return LL2NUM(*(const int64_t *)address);
606
+ }
607
+ if (strcmp(type, "unsigned long") == 0) {
608
+ return ULONG2NUM(*(const unsigned long *)address);
609
+ }
610
+ if (strcmp(type, "long") == 0) {
611
+ return LONG2NUM(*(const long *)address);
612
+ }
613
+ if (strcmp(type, "unsigned int") == 0 || strcmp(type, "uint32_t") == 0) {
614
+ return UINT2NUM(*(const uint32_t *)address);
615
+ }
616
+ if (strcmp(type, "int") == 0 || strcmp(type, "int32_t") == 0) {
617
+ return INT2NUM(*(const int32_t *)address);
618
+ }
619
+ if (strcmp(type, "unsigned short") == 0 || strcmp(type, "uint16_t") == 0) {
620
+ return UINT2NUM(*(const uint16_t *)address);
621
+ }
622
+ if (strcmp(type, "short") == 0 || strcmp(type, "int16_t") == 0) {
623
+ return INT2NUM(*(const int16_t *)address);
624
+ }
625
+ if (strcmp(type, "unsigned char") == 0 || strcmp(type, "uint8_t") == 0) {
626
+ return UINT2NUM(*(const uint8_t *)address);
627
+ }
628
+ if (strcmp(type, "signed char") == 0 || strcmp(type, "int8_t") == 0 || strcmp(type, "char") == 0) {
629
+ return INT2NUM(*(const int8_t *)address);
630
+ }
631
+ return Qnil;
632
+ }
633
+
634
+ static VALUE
635
+ field_names(VALUE self)
636
+ {
637
+ esrb_view_t *view = get_view(self);
638
+ VALUE names = rb_ary_new_capa((long)view->schema->field_count);
639
+ for (size_t index = 0; index < view->schema->field_count; index++) {
640
+ const esrb_field_t *field = &view->schema->fields[index];
641
+ rb_ary_push(names, ID2SYM(rb_intern(field->name)));
642
+ }
643
+ return names;
644
+ }
645
+
646
+ static VALUE
647
+ schema_name(VALUE self)
648
+ {
649
+ return rb_str_new_cstr(get_view(self)->schema->name);
650
+ }
651
+
652
+ static VALUE
653
+ warn_on_truncated_path(VALUE self)
654
+ {
655
+ return rb_funcall(get_view(self)->owner, rb_intern("__warn_on_truncated_path?"), 0);
656
+ }
657
+
658
+ static VALUE
659
+ exec_values(VALUE self, VALUE kind_value)
660
+ {
661
+ esrb_view_t *view = get_view(self);
662
+ if (strcmp(view->schema->name, "es_event_exec_t") != 0) {
663
+ return Qnil;
664
+ }
665
+ ID kind = SYM2ID(kind_value);
666
+ const es_event_exec_t *event = (const es_event_exec_t *)view->pointer;
667
+ uint32_t count;
668
+ VALUE values;
669
+ if (kind == rb_intern("args") || kind == rb_intern("env")) {
670
+ count = kind == rb_intern("args") ? es_exec_arg_count(event) : es_exec_env_count(event);
671
+ values = rb_ary_new_capa(count);
672
+ for (uint32_t index = 0; index < count; index++) {
673
+ es_string_token_t token = kind == rb_intern("args") ? es_exec_arg(event, index) : es_exec_env(event, index);
674
+ rb_ary_push(values, esrb_string_token_value(&token));
675
+ }
676
+ return values;
677
+ }
678
+ if (kind == rb_intern("fds")) {
679
+ count = es_exec_fd_count(event);
680
+ values = rb_ary_new_capa(count);
681
+ for (uint32_t index = 0; index < count; index++) {
682
+ rb_ary_push(values,
683
+ esrb_view_wrap(view->owner, es_exec_fd(event, index), "es_fd_t", view->message_version, view->strict_version));
684
+ }
685
+ return values;
686
+ }
687
+ return Qnil;
688
+ }
689
+
690
+ VALUE
691
+ esrb_event_wrap(VALUE owner, const es_message_t *message, bool strict_version)
692
+ {
693
+ for (size_t index = 0; index < esrb_event_schema_count; index++) {
694
+ const esrb_event_schema_t *event = &esrb_event_schemas[index];
695
+ if (event->event_type != message->event_type) {
696
+ continue;
697
+ }
698
+ const unsigned char *address = (const unsigned char *)&message->event + event->offset;
699
+ const void *pointer = event->indirect ? *(const void *const *)address : address;
700
+ return esrb_view_wrap(owner, pointer, event->schema, message->version, strict_version);
701
+ }
702
+ return Qnil;
703
+ }
704
+
705
+ void
706
+ esrb_init_field(VALUE endpoint_security)
707
+ {
708
+ c_native_view = rb_define_class_under(endpoint_security, "NativeView", rb_cObject);
709
+ rb_undef_alloc_func(c_native_view);
710
+ rb_define_method(c_native_view, "__read_field", read_field, 1);
711
+ rb_define_method(c_native_view, "__field_names", field_names, 0);
712
+ rb_define_method(c_native_view, "__schema_name", schema_name, 0);
713
+ rb_define_method(c_native_view, "__warn_on_truncated_path?", warn_on_truncated_path, 0);
714
+ rb_define_method(c_native_view, "__exec_values", exec_values, 1);
715
+ rb_define_class_under(endpoint_security, "Event", c_native_view);
716
+ rb_define_class_under(endpoint_security, "Process", c_native_view);
717
+ rb_define_class_under(endpoint_security, "File", c_native_view);
718
+ rb_define_class_under(endpoint_security, "Thread", c_native_view);
719
+ }