+static int write_boot_status_message(struct discover_server *server,
+ struct client *client, const struct status *status)
+{
+ struct pb_protocol_message *message;
+ int len;
+
+ len = pb_protocol_boot_status_len(status);
+
+ message = pb_protocol_create_message(client,
+ PB_PROTOCOL_ACTION_STATUS, len);
+ if (!message)
+ return -1;
+
+ pb_protocol_serialise_boot_status(status, message->payload, len);
+
+ return client_write_message(server, client, message);
+}
+
+static int write_system_info_message(struct discover_server *server,
+ struct client *client, const struct system_info *sysinfo)
+{
+ struct pb_protocol_message *message;
+ int len;
+
+ len = pb_protocol_system_info_len(sysinfo);
+
+ message = pb_protocol_create_message(client,
+ PB_PROTOCOL_ACTION_SYSTEM_INFO, len);
+ if (!message)
+ return -1;
+
+ pb_protocol_serialise_system_info(sysinfo, message->payload, len);
+
+ return client_write_message(server, client, message);
+}
+
+static int write_config_message(struct discover_server *server,
+ struct client *client, const struct config *config)
+{
+ struct pb_protocol_message *message;
+ int len;
+
+ len = pb_protocol_config_len(config);
+
+ message = pb_protocol_create_message(client,
+ PB_PROTOCOL_ACTION_CONFIG, len);
+ if (!message)
+ return -1;
+
+ pb_protocol_serialise_config(config, message->payload, len);
+
+ return client_write_message(server, client, message);
+}
+
+static int write_authenticate_message(struct discover_server *server,
+ struct client *client)
+{
+ struct pb_protocol_message *message;
+ struct auth_message auth_msg;
+ int len;
+
+ auth_msg.op = AUTH_MSG_RESPONSE;
+ auth_msg.authenticated = client->can_modify;
+
+ len = pb_protocol_authenticate_len(&auth_msg);
+
+ message = pb_protocol_create_message(client,
+ PB_PROTOCOL_ACTION_AUTHENTICATE, len);
+ if (!message)
+ return -1;
+
+ pb_protocol_serialise_authenticate(&auth_msg, message->payload, len);
+
+ return client_write_message(server, client, message);
+}
+
+static int client_auth_timeout(void *arg)
+{
+ struct client *client = arg;
+ int rc;
+
+ client->auth_waiter = NULL;
+ client->can_modify = false;
+
+ rc = write_authenticate_message(client->server, client);
+ if (rc)
+ pb_log("failed to send client auth timeout\n");
+
+ return 0;
+}
+
+static int discover_server_handle_auth_message(struct client *client,
+ struct auth_message *auth_msg)
+{
+ struct status *status;
+ char *hash;
+ int rc = 0;
+
+ status = talloc_zero(client, struct status);
+
+ switch (auth_msg->op) {
+ case AUTH_MSG_REQUEST:
+ if (!crypt_check_password(auth_msg->password)) {
+ rc = -1;
+ pb_log("Client failed to authenticate\n");
+ status->type = STATUS_ERROR;
+ status->message = talloc_asprintf(status,
+ _("Password incorrect"));
+ } else {
+ client->can_modify = true;
+ rc = write_authenticate_message(client->server,
+ client);
+ if (client->auth_waiter)
+ waiter_remove(client->auth_waiter);
+ client->auth_waiter = waiter_register_timeout(
+ client->server->waitset,
+ 300000, /* 5 min */
+ client_auth_timeout, client);
+ pb_log("Client authenticated\n");
+ status->type = STATUS_INFO;
+ status->message = talloc_asprintf(status,
+ _("Authenticated successfully"));
+ }
+ break;
+ case AUTH_MSG_SET:
+ if (client->server->restrict_clients) {
+ if (!crypt_check_password(auth_msg->set_password.password)) {
+ rc = -1;
+ pb_log("Wrong password for set request\n");
+ status->type = STATUS_ERROR;
+ status->message = talloc_asprintf(status,
+ _("Password incorrect"));
+ break;
+ }
+ }
+
+ rc = crypt_set_password(auth_msg,
+ auth_msg->set_password.new_password);
+ if (rc) {
+ pb_log("Failed to set password\n");
+ status->type = STATUS_ERROR;
+ status->message = talloc_asprintf(status,
+ _("Error setting password"));
+ } else {
+ if (!auth_msg->set_password.new_password ||
+ !strlen(auth_msg->set_password.new_password)) {
+ platform_set_password("");
+ discover_server_set_auth_mode(client->server,
+ false);
+ pb_log("Password cleared\n");
+ } else {
+ hash = crypt_get_hash(auth_msg);
+ platform_set_password(hash);
+ talloc_free(hash);
+ discover_server_set_auth_mode(client->server,
+ true);
+ }
+ pb_log("System password changed\n");
+ status->type = STATUS_ERROR;
+ status->message = talloc_asprintf(status,
+ _("Password updated successfully"));
+ }
+ break;
+ case AUTH_MSG_DECRYPT:
+ if (!client->can_modify) {
+ pb_log("Unauthenticated client tried to open encrypted device %s\n",
+ auth_msg->decrypt_dev.device_id);
+ rc = -1;
+ status->type = STATUS_ERROR;
+ status->message = talloc_asprintf(status,
+ _("Must authenticate before opening encrypted device"));
+ break;
+ }
+
+ device_handler_open_encrypted_dev(client->server->device_handler,
+ auth_msg->decrypt_dev.password,
+ auth_msg->decrypt_dev.device_id);
+ break;
+ default:
+ pb_log("%s: unknown op\n", __func__);
+ rc = -1;
+ break;
+ }
+
+ if (status->message)
+ write_boot_status_message(client->server, client, status);
+ talloc_free(status);
+
+ return rc;
+}
+
+static int discover_server_process_message(void *arg)
+{
+ struct autoboot_option *autoboot_opt;
+ struct pb_protocol_message *message;
+ struct boot_command *boot_command;
+ struct auth_message *auth_msg;
+ struct status *status;
+ struct client *client = arg;
+ struct config *config;
+ char *url;
+ int rc = 0;
+
+ message = pb_protocol_read_message(client, client->fd);
+
+ if (!message) {
+ talloc_free(client);
+ return 0;
+ }
+
+ /*
+ * If crypt support is enabled, non-authorised clients can only delay
+ * boot, not configure options or change the default boot option.
+ */
+ if (!client->can_modify) {
+ switch (message->action) {
+ case PB_PROTOCOL_ACTION_BOOT:
+ boot_command = talloc(client, struct boot_command);
+
+ rc = pb_protocol_deserialise_boot_command(boot_command,
+ message);
+ if (rc) {
+ pb_log("%s: no boot command?", __func__);
+ return 0;
+ }
+
+ device_handler_boot(client->server->device_handler,
+ client->can_modify, boot_command);
+ break;
+ case PB_PROTOCOL_ACTION_CANCEL_DEFAULT:
+ device_handler_cancel_default(client->server->device_handler);
+ break;
+ case PB_PROTOCOL_ACTION_AUTHENTICATE:
+ auth_msg = talloc(client, struct auth_message);
+ rc = pb_protocol_deserialise_authenticate(
+ auth_msg, message);
+ if (rc) {
+ pb_log("Couldn't parse client's auth request\n");
+ break;
+ }
+
+ rc = discover_server_handle_auth_message(client,
+ auth_msg);
+ talloc_free(auth_msg);
+ break;
+ default:
+ pb_log("non-root client tried to perform action %d\n",
+ message->action);
+ status = talloc_zero(client, struct status);
+ if (status) {
+ status->type = STATUS_ERROR;
+ status->message = talloc_asprintf(status,
+ "Client must run as root to make changes");
+ write_boot_status_message(client->server, client,
+ status);
+ talloc_free(status);
+ }
+ }
+ return rc;
+ }
+
+ switch (message->action) {
+ case PB_PROTOCOL_ACTION_BOOT:
+ boot_command = talloc(client, struct boot_command);
+
+ rc = pb_protocol_deserialise_boot_command(boot_command,
+ message);
+ if (rc) {
+ pb_log_fn("no boot command?\n");
+ return 0;
+ }
+
+ device_handler_boot(client->server->device_handler,
+ client->can_modify, boot_command);
+ break;
+
+ case PB_PROTOCOL_ACTION_CANCEL_DEFAULT:
+ device_handler_cancel_default(client->server->device_handler);
+ break;
+
+ case PB_PROTOCOL_ACTION_REINIT:
+ device_handler_reinit(client->server->device_handler);
+ break;
+
+ case PB_PROTOCOL_ACTION_CONFIG:
+ config = talloc_zero(client, struct config);
+
+ rc = pb_protocol_deserialise_config(config, message);
+ if (rc) {
+ pb_log_fn("no config?\n");
+ return 0;
+ }
+
+ device_handler_update_config(client->server->device_handler,
+ config);
+ break;
+
+ case PB_PROTOCOL_ACTION_ADD_URL:
+ url = pb_protocol_deserialise_string((void *) client, message);
+
+ device_handler_process_url(client->server->device_handler,
+ url, NULL, NULL);
+ break;
+
+ case PB_PROTOCOL_ACTION_PLUGIN_INSTALL:
+ url = pb_protocol_deserialise_string((void *) client, message);
+
+ device_handler_install_plugin(client->server->device_handler,
+ url);
+ break;
+
+ case PB_PROTOCOL_ACTION_TEMP_AUTOBOOT:
+ autoboot_opt = talloc_zero(client, struct autoboot_option);
+ rc = pb_protocol_deserialise_temp_autoboot(autoboot_opt,
+ message);
+ if (rc) {
+ pb_log("can't parse temporary autoboot message\n");
+ return 0;
+ }
+
+ device_handler_apply_temp_autoboot(
+ client->server->device_handler,
+ autoboot_opt);
+ break;
+
+ /* For AUTH_MSG_SET */
+ case PB_PROTOCOL_ACTION_AUTHENTICATE:
+ auth_msg = talloc(client, struct auth_message);
+ rc = pb_protocol_deserialise_authenticate(
+ auth_msg, message);
+ if (rc) {
+ pb_log("Couldn't parse client's auth request\n");
+ break;
+ }
+
+ discover_server_handle_auth_message(client, auth_msg);
+ talloc_free(auth_msg);
+ break;
+ default:
+ pb_log_fn("invalid action %d\n", message->action);
+ return 0;
+ }
+
+
+ return 0;
+}
+
+void discover_server_set_auth_mode(struct discover_server *server,
+ bool restrict_clients)
+{
+ struct client *client;
+
+ server->restrict_clients = restrict_clients;
+
+ list_for_each_entry(&server->clients, client, list) {
+ client->can_modify = !restrict_clients;
+ write_authenticate_message(server, client);
+ }
+}
+
+static int discover_server_process_connection(void *arg)