| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Functions corresponding to SET password methods under BIOS attributes interface GUID |
| * |
| * Copyright (c) 2020 Dell Inc. |
| */ |
| |
| #include <linux/wmi.h> |
| #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); |
| /* on success copy the new password to current password */ |
| if (!ret) |
| strscpy(current_password, new, 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 void 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); |
| } |
| |
| 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); |