// SPDX-License-Identifier: GPL-2.0 /* * Functions corresponding to SET password methods under BIOS attributes interface GUID * * Copyright (c) 2020 Dell Inc. */ #include #include "dell-wmi-sysman.h" static int call_password_interface(struct wmi_device *wdev, char *in_args, size_t size) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer input; union acpi_object *obj; acpi_status status; int ret = -EIO; input.length = (acpi_size) size; input.pointer = in_args; status = wmidev_evaluate_method(wdev, 0, 1, &input, &output); if (ACPI_FAILURE(status)) return -EIO; obj = (union acpi_object *)output.pointer; if (obj->type == ACPI_TYPE_INTEGER) ret = obj->integer.value; kfree(output.pointer); /* let userland know it may need to check is_password_set again */ kobject_uevent(&wmi_priv.class_dev->kobj, KOBJ_CHANGE); return map_wmi_error(ret); } /** * set_new_password() - Sets a system admin password * @password_type: The type of password to set * @new: The new password * * Sets the password using plaintext interface */ int set_new_password(const char *password_type, const char *new) { size_t password_type_size, current_password_size, new_size; size_t security_area_size, buffer_size; char *buffer = NULL, *start; char *current_password; int ret; mutex_lock(&wmi_priv.mutex); if (!wmi_priv.password_attr_wdev) { ret = -ENODEV; goto out; } if (strcmp(password_type, "Admin") == 0) { current_password = wmi_priv.current_admin_password; } else if (strcmp(password_type, "System") == 0) { current_password = wmi_priv.current_system_password; } else { ret = -EINVAL; dev_err(&wmi_priv.password_attr_wdev->dev, "unknown password type %s\n", password_type); goto out; } /* build/calculate buffer */ security_area_size = calculate_security_buffer(wmi_priv.current_admin_password); password_type_size = calculate_string_buffer(password_type); current_password_size = calculate_string_buffer(current_password); new_size = calculate_string_buffer(new); buffer_size = security_area_size + password_type_size + current_password_size + new_size; buffer = kzalloc(buffer_size, GFP_KERNEL); if (!buffer) { ret = -ENOMEM; goto out; } /* build security area */ populate_security_buffer(buffer, wmi_priv.current_admin_password); /* build variables to set */ start = buffer + security_area_size; ret = populate_string_buffer(start, password_type_size, password_type); if (ret < 0) goto out; start += ret; ret = populate_string_buffer(start, current_password_size, current_password); if (ret < 0) goto out; start += ret; ret = populate_string_buffer(start, new_size, new); if (ret < 0) goto out; print_hex_dump_bytes("set new password data: ", DUMP_PREFIX_NONE, buffer, buffer_size); ret = call_password_interface(wmi_priv.password_attr_wdev, buffer, buffer_size); /* clear current_password here and use user input from wmi_priv.current_password */ if (!ret) memset(current_password, 0, MAX_BUFF); /* explain to user the detailed failure reason */ else if (ret == -EOPNOTSUPP) dev_err(&wmi_priv.password_attr_wdev->dev, "admin password must be configured\n"); else if (ret == -EACCES) dev_err(&wmi_priv.password_attr_wdev->dev, "invalid password\n"); out: kfree(buffer); mutex_unlock(&wmi_priv.mutex); return ret; } static int bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *context) { mutex_lock(&wmi_priv.mutex); wmi_priv.password_attr_wdev = wdev; mutex_unlock(&wmi_priv.mutex); return 0; } static int bios_attr_pass_interface_remove(struct wmi_device *wdev) { mutex_lock(&wmi_priv.mutex); wmi_priv.password_attr_wdev = NULL; mutex_unlock(&wmi_priv.mutex); return 0; } static const struct wmi_device_id bios_attr_pass_interface_id_table[] = { { .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID }, { }, }; static struct wmi_driver bios_attr_pass_interface_driver = { .driver = { .name = DRIVER_NAME"-password" }, .probe = bios_attr_pass_interface_probe, .remove = bios_attr_pass_interface_remove, .id_table = bios_attr_pass_interface_id_table, }; int init_bios_attr_pass_interface(void) { return wmi_driver_register(&bios_attr_pass_interface_driver); } void exit_bios_attr_pass_interface(void) { wmi_driver_unregister(&bios_attr_pass_interface_driver); } MODULE_DEVICE_TABLE(wmi, bios_attr_pass_interface_id_table);