197 lines
5.1 KiB
C
197 lines
5.1 KiB
C
|
// 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;
|
||
|
}
|