| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Functions corresponding to password object type attributes under BIOS Password Object GUID for |
| * use with dell-wmi-sysman |
| * |
| * Copyright (c) 2020 Dell Inc. |
| */ |
| |
| #include "dell-wmi-sysman.h" |
| |
| enum po_properties {IS_PASS_SET = 1, MIN_PASS_LEN, MAX_PASS_LEN}; |
| |
| get_instance_id(po); |
| |
| static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, |
| char *buf) |
| { |
| int instance_id = get_po_instance_id(kobj); |
| union acpi_object *obj; |
| ssize_t ret; |
| |
| if (instance_id < 0) |
| return instance_id; |
| |
| /* need to use specific instance_id and guid combination to get right data */ |
| obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); |
| if (!obj) |
| return -EIO; |
| if (obj->package.elements[IS_PASS_SET].type != ACPI_TYPE_INTEGER) { |
| kfree(obj); |
| return -EINVAL; |
| } |
| ret = snprintf(buf, PAGE_SIZE, "%lld\n", obj->package.elements[IS_PASS_SET].integer.value); |
| kfree(obj); |
| return ret; |
| } |
| |
| static struct kobj_attribute po_is_pass_set = __ATTR_RO(is_enabled); |
| |
| static ssize_t current_password_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, size_t count) |
| { |
| char *target = NULL; |
| int length; |
| |
| length = strlen(buf); |
| if (buf[length-1] == '\n') |
| length--; |
| |
| /* firmware does verifiation of min/max password length, |
| * hence only check for not exceeding MAX_BUFF here. |
| */ |
| if (length >= MAX_BUFF) |
| return -EINVAL; |
| |
| if (strcmp(kobj->name, "Admin") == 0) |
| target = wmi_priv.current_admin_password; |
| else if (strcmp(kobj->name, "System") == 0) |
| target = wmi_priv.current_system_password; |
| if (!target) |
| return -EIO; |
| memcpy(target, buf, length); |
| target[length] = '\0'; |
| |
| return count; |
| } |
| |
| static struct kobj_attribute po_current_password = __ATTR_WO(current_password); |
| |
| static ssize_t new_password_store(struct kobject *kobj, |
| struct kobj_attribute *attr, |
| const char *buf, size_t count) |
| { |
| char *p, *buf_cp; |
| int ret; |
| |
| buf_cp = kstrdup(buf, GFP_KERNEL); |
| if (!buf_cp) |
| return -ENOMEM; |
| p = memchr(buf_cp, '\n', count); |
| |
| if (p != NULL) |
| *p = '\0'; |
| if (strlen(buf_cp) > MAX_BUFF) { |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| ret = set_new_password(kobj->name, buf_cp); |
| |
| out: |
| kfree(buf_cp); |
| return ret ? ret : count; |
| } |
| |
| static struct kobj_attribute po_new_password = __ATTR_WO(new_password); |
| |
| attribute_n_property_show(min_password_length, po); |
| static struct kobj_attribute po_min_pass_length = __ATTR_RO(min_password_length); |
| |
| attribute_n_property_show(max_password_length, po); |
| static struct kobj_attribute po_max_pass_length = __ATTR_RO(max_password_length); |
| |
| static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, |
| char *buf) |
| { |
| return sprintf(buf, "password\n"); |
| } |
| |
| static struct kobj_attribute po_mechanism = __ATTR_RO(mechanism); |
| |
| static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, |
| char *buf) |
| { |
| if (strcmp(kobj->name, "Admin") == 0) |
| return sprintf(buf, "bios-admin\n"); |
| else if (strcmp(kobj->name, "System") == 0) |
| return sprintf(buf, "power-on\n"); |
| return -EIO; |
| } |
| |
| static struct kobj_attribute po_role = __ATTR_RO(role); |
| |
| static struct attribute *po_attrs[] = { |
| &po_is_pass_set.attr, |
| &po_min_pass_length.attr, |
| &po_max_pass_length.attr, |
| &po_current_password.attr, |
| &po_new_password.attr, |
| &po_role.attr, |
| &po_mechanism.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group po_attr_group = { |
| .attrs = po_attrs, |
| }; |
| |
| int alloc_po_data(void) |
| { |
| int ret = 0; |
| |
| wmi_priv.po_instances_count = get_instance_count(DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); |
| wmi_priv.po_data = kcalloc(wmi_priv.po_instances_count, sizeof(struct po_data), GFP_KERNEL); |
| if (!wmi_priv.po_data) { |
| wmi_priv.po_instances_count = 0; |
| ret = -ENOMEM; |
| } |
| return ret; |
| } |
| |
| /** |
| * populate_po_data() - Populate all properties of an instance under password object attribute |
| * @po_obj: ACPI object with password object data |
| * @instance_id: The instance to enumerate |
| * @attr_name_kobj: The parent kernel object |
| */ |
| int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj) |
| { |
| wmi_priv.po_data[instance_id].attr_name_kobj = attr_name_kobj; |
| if (check_property_type(po, ATTR_NAME, ACPI_TYPE_STRING)) |
| return -EINVAL; |
| strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name, |
| po_obj[ATTR_NAME].string.pointer); |
| if (check_property_type(po, MIN_PASS_LEN, ACPI_TYPE_INTEGER)) |
| return -EINVAL; |
| wmi_priv.po_data[instance_id].min_password_length = |
| (uintptr_t)po_obj[MIN_PASS_LEN].string.pointer; |
| if (check_property_type(po, MAX_PASS_LEN, ACPI_TYPE_INTEGER)) |
| return -EINVAL; |
| wmi_priv.po_data[instance_id].max_password_length = |
| (uintptr_t) po_obj[MAX_PASS_LEN].string.pointer; |
| |
| return sysfs_create_group(attr_name_kobj, &po_attr_group); |
| } |
| |
| /** |
| * exit_po_attributes() - Clear all attribute data |
| * |
| * Clears all data allocated for this group of attributes |
| */ |
| void exit_po_attributes(void) |
| { |
| int instance_id; |
| |
| for (instance_id = 0; instance_id < wmi_priv.po_instances_count; instance_id++) { |
| if (wmi_priv.po_data[instance_id].attr_name_kobj) |
| sysfs_remove_group(wmi_priv.po_data[instance_id].attr_name_kobj, |
| &po_attr_group); |
| } |
| wmi_priv.po_instances_count = 0; |
| |
| kfree(wmi_priv.po_data); |
| wmi_priv.po_data = NULL; |
| } |