Example #1
0
static void client_handle_authenticate_request(Client *client,
        AuthenticateRequest *request) {
    uint32_t nonces[2];
    uint8_t digest[SHA1_DIGEST_LENGTH];
    const char *secret;
    char packet_signature[PACKET_MAX_SIGNATURE_LENGTH];
    AuthenticateResponse response;

    if (client->authentication_state == CLIENT_AUTHENTICATION_STATE_DISABLED) {
        log_error("Client ("CLIENT_SIGNATURE_FORMAT") tries to authenticate, but authentication is disabled, disconnecting client",
                  client_expand_signature(client));

        client->disconnected = true;

        return;
    }

    if (client->authentication_state != CLIENT_AUTHENTICATION_STATE_NONCE_SEND) {
        log_error("Client ("CLIENT_SIGNATURE_FORMAT") performed invalid authentication sequence (%s -> %s), disconnecting client",
                  client_expand_signature(client),
                  client_get_authentication_state_name(client->authentication_state),
                  client_get_authentication_state_name(CLIENT_AUTHENTICATION_STATE_DONE));

        client->disconnected = true;

        return;
    }

    memcpy(&nonces[0], &client->authentication_nonce, sizeof(client->authentication_nonce));
    memcpy(&nonces[1], request->client_nonce, sizeof(request->client_nonce));

    secret = config_get_option_value("authentication.secret")->string;

    hmac_sha1((uint8_t *)secret, strlen(secret),
              (uint8_t *)nonces, sizeof(nonces), digest);

    if (memcmp(request->digest, digest, SHA1_DIGEST_LENGTH) != 0) {
        log_error("Authenticate request (%s) from client ("CLIENT_SIGNATURE_FORMAT") did not contain the expected data, disconnecting client",
                  packet_get_request_signature(packet_signature, (Packet *)request),
                  client_expand_signature(client));

        client->disconnected = true;

        return;
    }

    client->authentication_state = CLIENT_AUTHENTICATION_STATE_DONE;

    log_info("Client ("CLIENT_SIGNATURE_FORMAT") successfully finished authentication",
             client_expand_signature(client));

    if (packet_header_get_response_expected(&request->header)) {
        response.header = request->header;
        response.header.length = sizeof(response);

        packet_header_set_error_code(&response.header, PACKET_E_SUCCESS);

        client_dispatch_response(client, NULL, (Packet *)&response, false, false);
    }
}
Example #2
0
void client_destroy(Client *client) {
    bool destroy_pending_requests = false;
    PendingRequest *pending_request;

    if (client->pending_request_count > 0) {
        log_warn("Destroying client ("CLIENT_SIGNATURE_FORMAT") while %d request(s) are still pending",
                 client_expand_signature(client), client->pending_request_count);

        if (network_create_zombie(client) < 0) {
            log_error("Could not create zombie for %d pending request(s) of ("CLIENT_SIGNATURE_FORMAT")",
                      client->pending_request_count, client_expand_signature(client));

            destroy_pending_requests = true;
        }
    }

    writer_destroy(&client->response_writer);

    event_remove_source(client->io->handle, EVENT_SOURCE_TYPE_GENERIC);
    io_destroy(client->io);
    free(client->io);

    if (destroy_pending_requests) {
        while (client->pending_request_sentinel.next != &client->pending_request_sentinel) {
            pending_request = containerof(client->pending_request_sentinel.next, PendingRequest, client_node);

            pending_request_remove_and_free(pending_request);
        }
    }

    if (client->destroy_done != NULL) {
        client->destroy_done();
    }
}
Example #3
0
static void client_handle_request(Client *client, Packet *request) {
    char packet_signature[PACKET_MAX_SIGNATURE_LENGTH];
    EmptyResponse response;

    // handle requests meant for brickd
    if (uint32_from_le(request->header.uid) == UID_BRICK_DAEMON) {
        // add as pending request if response is expected
        if (packet_header_get_response_expected(&request->header)) {
            network_client_expects_response(client, request);
        }

        if (request->header.function_id == FUNCTION_GET_AUTHENTICATION_NONCE) {
            if (request->header.length != sizeof(GetAuthenticationNonceRequest)) {
                log_error("Received authentication-nonce request (%s) from client ("CLIENT_SIGNATURE_FORMAT") with wrong length, disconnecting client",
                          packet_get_request_signature(packet_signature, request),
                          client_expand_signature(client));

                client->disconnected = true;

                return;
            }

            client_handle_get_authentication_nonce_request(client, (GetAuthenticationNonceRequest *)request);
        } else if (request->header.function_id == FUNCTION_AUTHENTICATE) {
            if (request->header.length != sizeof(AuthenticateRequest)) {
                log_error("Received authenticate request (%s) from client ("CLIENT_SIGNATURE_FORMAT") with wrong length, disconnecting client",
                          packet_get_request_signature(packet_signature, request),
                          client_expand_signature(client));

                client->disconnected = true;

                return;
            }

            client_handle_authenticate_request(client, (AuthenticateRequest *)request);
        } else {
            response.header = request->header;
            response.header.length = sizeof(response);

            packet_header_set_error_code(&response.header,
                                         PACKET_E_FUNCTION_NOT_SUPPORTED);

            client_dispatch_response(client, NULL, (Packet *)&response, false, false);
        }
    } else if (client->authentication_state == CLIENT_AUTHENTICATION_STATE_DISABLED ||
               client->authentication_state == CLIENT_AUTHENTICATION_STATE_DONE) {
        // add as pending request if response is expected...
        if (packet_header_get_response_expected(&request->header)) {
            network_client_expects_response(client, request);
        }

        // ...then dispatch it to the hardware
        hardware_dispatch_request(request);
    } else {
        log_packet_debug("Client ("CLIENT_SIGNATURE_FORMAT") is not authenticated, dropping request (%s)",
                         client_expand_signature(client),
                         packet_get_request_signature(packet_signature, request));
    }
}
Example #4
0
Client *network_create_client(const char *name, IO *io) {
	Client *client;

	// append to client array
	client = array_append(&_clients);

	if (client == NULL) {
		log_error("Could not append to client array: %s (%d)",
		          get_errno_name(errno), errno);

		return NULL;
	}

	// create new client that takes ownership of the I/O object
	if (client_create(client, name, io, _next_authentication_nonce++, NULL) < 0) {
		array_remove(&_clients, _clients.count - 1, NULL);

		return NULL;
	}

	log_info("Added new client ("CLIENT_SIGNATURE_FORMAT")",
	         client_expand_signature(client));

	return client;
}
Example #5
0
// remove clients that got marked as disconnected and finished zombies
void network_cleanup_clients_and_zombies(void) {
	int i;
	Client *client;
	Zombie *zombie;

	// iterate backwards for simpler index handling
	for (i = _clients.count - 1; i >= 0; --i) {
		client = array_get(&_clients, i);

		if (client->disconnected) {
			log_debug("Removing disconnected client ("CLIENT_SIGNATURE_FORMAT")",
			          client_expand_signature(client));

			array_remove(&_clients, i, (ItemDestroyFunction)client_destroy);
		}
	}

	// iterate backwards for simpler index handling
	for (i = _zombies.count - 1; i >= 0; --i) {
		zombie = array_get(&_zombies, i);

		if (zombie->finished) {
			log_debug("Removing finished zombie (id: %u)", zombie->id);

			array_remove(&_zombies, i, (ItemDestroyFunction)zombie_destroy);
		}
	}
}
Example #6
0
void network_client_expects_response(Client *client, Packet *request) {
	PendingRequest *pending_request;
	char packet_signature[PACKET_MAX_SIGNATURE_LENGTH];

	if (client->pending_request_count >= CLIENT_MAX_PENDING_REQUESTS) {
		log_warn("Pending requests list for client ("CLIENT_SIGNATURE_FORMAT") is full, dropping %d pending request(s)",
		         client_expand_signature(client),
		         client->pending_request_count - CLIENT_MAX_PENDING_REQUESTS + 1);

		while (client->pending_request_count >= CLIENT_MAX_PENDING_REQUESTS) {
			pending_request = containerof(client->pending_request_sentinel.next, PendingRequest, client_node);

			pending_request_remove_and_free(pending_request);
		}
	}

	pending_request = calloc(1, sizeof(PendingRequest));

	if (pending_request == NULL) {
		log_error("Could not allocate pending request: %s (%d)",
		          get_errno_name(ENOMEM), ENOMEM);

		return;
	}

	node_reset(&pending_request->global_node);
	node_insert_before(&_pending_request_sentinel, &pending_request->global_node);

	node_reset(&pending_request->client_node);
	node_insert_before(&client->pending_request_sentinel, &pending_request->client_node);

	++client->pending_request_count;

	pending_request->client = client;
	pending_request->zombie = NULL;

	memcpy(&pending_request->header, &request->header, sizeof(PacketHeader));

#ifdef BRICKD_WITH_PROFILING
	pending_request->arrival_time = microseconds();
#endif

	log_packet_debug("Added pending request (%s) for client ("CLIENT_SIGNATURE_FORMAT")",
	                 packet_get_request_signature(packet_signature, request),
	                 client_expand_signature(client));
}
Example #7
0
static char *client_get_recipient_signature(char *signature, bool upper, void *opaque) {
    Client *client = opaque;

    snprintf(signature, WRITER_MAX_RECIPIENT_SIGNATURE_LENGTH,
             "%client ("CLIENT_SIGNATURE_FORMAT")",
             upper ? 'C' : 'c', client_expand_signature(client));

    return signature;
}
Example #8
0
static void client_handle_get_authentication_nonce_request(Client *client,
        GetAuthenticationNonceRequest *request) {
    GetAuthenticationNonceResponse response;

    if (client->authentication_state == CLIENT_AUTHENTICATION_STATE_DISABLED) {
        log_error("Client ("CLIENT_SIGNATURE_FORMAT") tries to authenticate, but authentication is disabled, disconnecting client",
                  client_expand_signature(client));

        client->disconnected = true;

        return;
    }

    if (client->authentication_state == CLIENT_AUTHENTICATION_STATE_DONE) {
        log_debug("Already authenticated client ("CLIENT_SIGNATURE_FORMAT") tries to authenticate again",
                  client_expand_signature(client));

        client->authentication_state = CLIENT_AUTHENTICATION_STATE_ENABLED;
    }

    if (client->authentication_state != CLIENT_AUTHENTICATION_STATE_ENABLED) {
        log_error("Client ("CLIENT_SIGNATURE_FORMAT") performed invalid authentication sequence (%s -> %s), disconnecting client",
                  client_expand_signature(client),
                  client_get_authentication_state_name(client->authentication_state),
                  client_get_authentication_state_name(CLIENT_AUTHENTICATION_STATE_NONCE_SEND));

        client->disconnected = true;

        return;
    }

    response.header = request->header;
    response.header.length = sizeof(response);

    memcpy(response.server_nonce, &client->authentication_nonce,
           sizeof(response.server_nonce));

    client_dispatch_response(client, NULL, (Packet *)&response, false, true);

    client->authentication_state = CLIENT_AUTHENTICATION_STATE_NONCE_SEND;
}
Example #9
0
void client_dispatch_response(Client *client, PendingRequest *pending_request,
                              Packet *response, bool force, bool ignore_authentication) {
    Node *pending_request_client_node = NULL;
    int enqueued = 0;
#ifdef BRICKD_WITH_PROFILING
    uint64_t elapsed;
#endif

    if (!ignore_authentication &&
            client->authentication_state != CLIENT_AUTHENTICATION_STATE_DISABLED &&
            client->authentication_state != CLIENT_AUTHENTICATION_STATE_DONE) {
        log_packet_debug("Ignoring non-authenticated client ("CLIENT_SIGNATURE_FORMAT")",
                         client_expand_signature(client));

        goto cleanup;
    }

    // find matching pending request if not forced and no pending request is
    // already given. do this before the disconnect check to ensure that even
    // for a disconnected client the pending request list is updated correctly
    if (!force && pending_request == NULL) {
        pending_request_client_node = client->pending_request_sentinel.next;

        while (pending_request_client_node != &client->pending_request_sentinel) {
            pending_request = containerof(pending_request_client_node, PendingRequest, client_node);

            if (packet_is_matching_response(response, &pending_request->header)) {
                break;
            }

            pending_request_client_node = pending_request_client_node->next;
        }

        if (pending_request_client_node == &client->pending_request_sentinel) {
            pending_request = NULL;

            goto cleanup;
        }
    }

    if (client->disconnected) {
        log_debug("Ignoring disconnected client ("CLIENT_SIGNATURE_FORMAT")",
                  client_expand_signature(client));

        goto cleanup;
    }

    if (force || pending_request != NULL) {
        enqueued = writer_write(&client->response_writer, response);

        if (enqueued < 0) {
            goto cleanup;
        }

        if (force) {
            log_packet_debug("Forced to %s response to client ("CLIENT_SIGNATURE_FORMAT")",
                             enqueued ? "enqueue" : "send", client_expand_signature(client));
        } else {
#ifdef BRICKD_WITH_PROFILING
            elapsed = microseconds() - pending_request->arrival_time;

            log_packet_debug("%s response to client ("CLIENT_SIGNATURE_FORMAT"), was requested %u.%03u msec ago, %d request(s) still pending",
                             enqueued ? "Enqueued" : "Sent", client_expand_signature(client),
                             (unsigned int)(elapsed / 1000), (unsigned int)(elapsed % 1000),
                             client->pending_request_count - 1);
#else
            log_packet_debug("%s response to client ("CLIENT_SIGNATURE_FORMAT"), %d request(s) still pending",
                             enqueued ? "Enqueued" : "Sent", client_expand_signature(client),
                             client->pending_request_count - 1);
#endif
        }
    }

cleanup:
    if (pending_request != NULL) {
        pending_request_remove_and_free(pending_request);
    }
}
Example #10
0
static void client_handle_read(void *opaque) {
    Client *client = opaque;
    int length;
    const char *message = NULL;
    char packet_signature[PACKET_MAX_SIGNATURE_LENGTH];

    length = io_read(client->io, (uint8_t *)&client->request + client->request_used,
                     sizeof(Packet) - client->request_used);

    if (length == 0) {
        log_info("Client ("CLIENT_SIGNATURE_FORMAT") disconnected by peer",
                 client_expand_signature(client));

        client->disconnected = true;

        return;
    }

    if (length < 0) {
        if (length == IO_CONTINUE) {
            // no actual data received
        } else if (errno_interrupted()) {
            log_debug("Receiving from client ("CLIENT_SIGNATURE_FORMAT") was interrupted, retrying",
                      client_expand_signature(client));
        } else if (errno_would_block()) {
            log_debug("Receiving from client ("CLIENT_SIGNATURE_FORMAT") would block, retrying",
                      client_expand_signature(client));
        } else {
            log_error("Could not receive from client ("CLIENT_SIGNATURE_FORMAT"), disconnecting client: %s (%d)",
                      client_expand_signature(client), get_errno_name(errno), errno);

            client->disconnected = true;
        }

        return;
    }

    client->request_used += length;

    while (!client->disconnected && client->request_used > 0) {
        if (client->request_used < (int)sizeof(PacketHeader)) {
            // wait for complete header
            break;
        }

        if (!client->request_header_checked) {
            if (!packet_header_is_valid_request(&client->request.header, &message)) {
                // FIXME: include packet_get_content_dump output in the error message
                log_error("Received invalid request (%s) from client ("CLIENT_SIGNATURE_FORMAT"), disconnecting client: %s",
                          packet_get_request_signature(packet_signature, &client->request),
                          client_expand_signature(client), message);

                client->disconnected = true;

                return;
            }

            client->request_header_checked = true;
        }

        length = client->request.header.length;

        if (client->request_used < length) {
            // wait for complete packet
            break;
        }

        if (client->request.header.function_id == FUNCTION_DISCONNECT_PROBE) {
            log_packet_debug("Received disconnect probe from client ("CLIENT_SIGNATURE_FORMAT"), dropping request",
                             client_expand_signature(client));
        } else {
            log_packet_debug("Received request (%s) from client ("CLIENT_SIGNATURE_FORMAT")",
                             packet_get_request_signature(packet_signature, &client->request),
                             client_expand_signature(client));

            client_handle_request(client, &client->request);
        }

        memmove(&client->request, (uint8_t *)&client->request + length,
                client->request_used - length);

        client->request_used -= length;
        client->request_header_checked = false;
    }
}