#include #include #include #include #include "match.h" #include "util.h" #include "wire.h" #include "try.h" #include "server.h" void match_rule_free(MatchRule *rule) { if (rule) { free(rule->rule_string); free(rule->sender); free(rule->interface); free(rule->member); free(rule->path); free(rule->path_namespace); free(rule->destination); free(rule->arg0namespace); for (int i = 0; i < MATCH_RULE_MAX_ARG; i++) { free(rule->arg[i]); free(rule->arg_path[i]); } free(rule); } } MatchRule *match_rule_from_string(char *d) { char *key = NULL; char acc[MATCH_RULE_MAX]; int acci = 0; MatchRule *rule; if (strlen(d) >= MATCH_RULE_MAX) { return NULL; } rule = calloc(sizeof(MatchRule), 1); if (!rule) { return NULL; } rule->rule_string = string_dup(d); while (*d) { /* key */ key = d; while (*d && *d != '=') d++; if (*d != '=') { goto fail; } *d = '\0'; d++; if (!*d) { goto fail; } /* value */ bool quote = false; acci = 0; acc[acci] = '\0'; for (;;) { if (*d == '\'') { /* '\'' starts a quoted region */ quote = !quote; d++; } else if (!quote && *d == ',') { /* ',' outside quoted region means next key-value pair */ acc[acci] = '\0'; acci = 0; d++; break; } else if (!quote && *d == '\\') { /* '\\' outside quoted region means escaped character */ /* TODO: not entirely to spec */ d++; if (*d != '\'') { goto fail; } if (acci >= MATCH_RULE_MAX) { goto fail; } acc[acci++] = *d; d++; } else if (!*d) { /* end of string */ if (quote) { /* end of string, yet we were insinde a quoted region */ /* unexpected end of input */ goto fail; } acc[acci] = '\0'; acci = 0; break; } else if (quote) { if (acci >= MATCH_RULE_MAX) { goto fail; } acc[acci++] = *d; d++; } else { goto fail; } } if (strcmp(key, "type") == 0 && !rule->type) { if (strcmp(acc, "signal") == 0) rule->type = DBUS_MESSAGE_SIGNAL; else if (strcmp(acc, "method_call") == 0) rule->type = DBUS_MESSAGE_METHOD_CALL; else if (strcmp(acc, "method_return") == 0) rule->type = DBUS_MESSAGE_METHOD_RETURN; else if (strcmp(acc, "error") == 0) rule->type = DBUS_MESSAGE_ERROR; else goto fail; } else if (strcmp(key, "sender") == 0 && !rule->sender) { rule->sender = string_dup(acc); } else if (strcmp(key, "interface") == 0 && !rule->interface) { rule->interface = string_dup(acc); } else if (strcmp(key, "member") == 0 && !rule->member) { rule->member = string_dup(acc); } else if (strcmp(key, "path") == 0 && !rule->path && !rule->path_namespace) { rule->path = string_dup(acc); } else if (strcmp(key, "path_namespace") == 0 && !rule->path && !rule->path_namespace) { rule->path_namespace = string_dup(acc); } else if (strcmp(key, "destination") == 0 && !rule->destination) { rule->destination = string_dup(acc); } else if (strcmp(key, "arg0namespace") == 0 && !rule->arg0namespace) { rule->arg0namespace = string_dup(acc); } else if (strncmp(key, "arg", 3) == 0) { char *part = key + 3; if (!*part) { goto fail; } // thanks: https://github.com/bus1/dbus-broker/blob/55fe5443d88ee2b93afa340b82ad89508ff017e2/src/bus/match.c#L105 size_t partn = strlen(part); uint32_t index = 0; for (unsigned int i = 0; i < 2 && partn; i++, part++, --partn) { if (*part >= '0' && *part <= '9') { index = index * 10 + *part - '0'; } else { break; } } if (index == 0 && rule->arg0namespace) { goto fail; } if (index >= MATCH_RULE_MAX_ARG) { goto fail; } if (rule->arg[index] || rule->arg_path[index]) { goto fail; } if (*part == '\0') { rule->arg[index] = string_dup(acc); } else if (strcmp(part, "path") == 0) { rule->arg_path[index] = string_dup(acc); } else { goto fail; } } else { goto fail; } } return rule; fail: match_rule_free(rule); return NULL; } #define _check_header_field_str(M_rule_field, M_header_field) \ do { \ if (rule->M_rule_field && (!msg->fields[M_header_field].present || strcmp(rule->M_rule_field, msg->fields[M_header_field].t.str) != 0)) { \ return -1; \ } \ } while(0) int match_check_sender(Bus *s, BusClient *sender_client, MatchRule *rule) { if (rule->sender) { if (sender_client->unique_name) { if (strcmp(rule->sender, sender_client->unique_name->name) == 0) { return 1; } } for (int i = 0; i < BUS_NAMES_PER_CLIENT; i++) { if (sender_client->owned_names[i] && strcmp(rule->sender, sender_client->owned_names[i]->name) == 0) { return 1; } } } return -1; } int match_rule_check(Bus *s, BusClient *sender_client, MatchRule *rule, WireMsg *msg, WireCtx *ctx) { if (rule->type && msg->type != rule->type) { return -1; } if (!sender_client) { // if the sender is negative, we assume the message is coming from the message bus if (rule->sender && strcmp(rule->sender, "org.freedesktop.DBus") != 0) { return -1; } } else { if (match_check_sender(s, sender_client, rule) < 0) { return -1; } } _check_header_field_str(interface, DBUS_HEADER_FIELD_INTERFACE); _check_header_field_str(member, DBUS_HEADER_FIELD_MEMBER); _check_header_field_str(path, DBUS_HEADER_FIELD_PATH); /* todo: path_namespace */ _check_header_field_str(destination, DBUS_HEADER_FIELD_DESTINATION); WireMsgBodyString strings[MATCH_RULE_MAX_ARG]; memset(strings, 0, sizeof(strings)); /* TODO: handle failure */ TRYST(wire_collect_strings(ctx, msg, strings, MATCH_RULE_MAX_ARG)); for (int i = 0; i < MATCH_RULE_MAX_ARG; i++) { if (rule->arg[i] && (!strings[i].str || strcmp(strings[i].str, rule->arg[i]) != 0)) { return -1; } /* todo: arg_path */ } /* todo: arg0namespace */ return 0; }