1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Functions corresponding to password object type attributes under BIOS Password Object GUID for
4  * use with dell-wmi-sysman
5  *
6  *  Copyright (c) 2020 Dell Inc.
7  */
8 
9 #include "dell-wmi-sysman.h"
10 
11 enum po_properties {IS_PASS_SET = 1, MIN_PASS_LEN, MAX_PASS_LEN};
12 
13 get_instance_id(po);
14 
is_enabled_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)15 static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr,
16 					  char *buf)
17 {
18 	int instance_id = get_po_instance_id(kobj);
19 	union acpi_object *obj;
20 	ssize_t ret;
21 
22 	if (instance_id < 0)
23 		return instance_id;
24 
25 	/* need to use specific instance_id and guid combination to get right data */
26 	obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
27 	if (!obj)
28 		return -EIO;
29 	if (obj->package.elements[IS_PASS_SET].type != ACPI_TYPE_INTEGER) {
30 		kfree(obj);
31 		return -EINVAL;
32 	}
33 	ret = snprintf(buf, PAGE_SIZE, "%lld\n", obj->package.elements[IS_PASS_SET].integer.value);
34 	kfree(obj);
35 	return ret;
36 }
37 
38 static struct kobj_attribute po_is_pass_set = __ATTR_RO(is_enabled);
39 
current_password_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)40 static ssize_t current_password_store(struct kobject *kobj,
41 				      struct kobj_attribute *attr,
42 				      const char *buf, size_t count)
43 {
44 	char *target = NULL;
45 	int length;
46 
47 	length = strlen(buf);
48 	if (buf[length-1] == '\n')
49 		length--;
50 
51 	/* firmware does verifiation of min/max password length,
52 	 * hence only check for not exceeding MAX_BUFF here.
53 	 */
54 	if (length >= MAX_BUFF)
55 		return -EINVAL;
56 
57 	if (strcmp(kobj->name, "Admin") == 0)
58 		target = wmi_priv.current_admin_password;
59 	else if (strcmp(kobj->name, "System") == 0)
60 		target = wmi_priv.current_system_password;
61 	if (!target)
62 		return -EIO;
63 	memcpy(target, buf, length);
64 	target[length] = '\0';
65 
66 	return count;
67 }
68 
69 static struct kobj_attribute po_current_password = __ATTR_WO(current_password);
70 
new_password_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)71 static ssize_t new_password_store(struct kobject *kobj,
72 				  struct kobj_attribute *attr,
73 				  const char *buf, size_t count)
74 {
75 	char *p, *buf_cp;
76 	int ret;
77 
78 	buf_cp = kstrdup(buf, GFP_KERNEL);
79 	if (!buf_cp)
80 		return -ENOMEM;
81 	p = memchr(buf_cp, '\n', count);
82 
83 	if (p != NULL)
84 		*p = '\0';
85 	if (strlen(buf_cp) > MAX_BUFF) {
86 		ret = -EINVAL;
87 		goto out;
88 	}
89 
90 	ret = set_new_password(kobj->name, buf_cp);
91 
92 out:
93 	kfree(buf_cp);
94 	return ret ? ret : count;
95 }
96 
97 static struct kobj_attribute po_new_password = __ATTR_WO(new_password);
98 
99 attribute_n_property_show(min_password_length, po);
100 static struct kobj_attribute po_min_pass_length = __ATTR_RO(min_password_length);
101 
102 attribute_n_property_show(max_password_length, po);
103 static struct kobj_attribute po_max_pass_length = __ATTR_RO(max_password_length);
104 
mechanism_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)105 static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr,
106 			 char *buf)
107 {
108 	return sprintf(buf, "password\n");
109 }
110 
111 static struct kobj_attribute po_mechanism = __ATTR_RO(mechanism);
112 
role_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)113 static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr,
114 			 char *buf)
115 {
116 	if (strcmp(kobj->name, "Admin") == 0)
117 		return sprintf(buf, "bios-admin\n");
118 	else if (strcmp(kobj->name, "System") == 0)
119 		return sprintf(buf, "power-on\n");
120 	return -EIO;
121 }
122 
123 static struct kobj_attribute po_role = __ATTR_RO(role);
124 
125 static struct attribute *po_attrs[] = {
126 	&po_is_pass_set.attr,
127 	&po_min_pass_length.attr,
128 	&po_max_pass_length.attr,
129 	&po_current_password.attr,
130 	&po_new_password.attr,
131 	&po_role.attr,
132 	&po_mechanism.attr,
133 	NULL,
134 };
135 
136 static const struct attribute_group po_attr_group = {
137 	.attrs = po_attrs,
138 };
139 
alloc_po_data(void)140 int alloc_po_data(void)
141 {
142 	int ret = 0;
143 
144 	wmi_priv.po_instances_count = get_instance_count(DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
145 	wmi_priv.po_data = kcalloc(wmi_priv.po_instances_count, sizeof(struct po_data), GFP_KERNEL);
146 	if (!wmi_priv.po_data) {
147 		wmi_priv.po_instances_count = 0;
148 		ret = -ENOMEM;
149 	}
150 	return ret;
151 }
152 
153 /**
154  * populate_po_data() - Populate all properties of an instance under password object attribute
155  * @po_obj: ACPI object with password object data
156  * @instance_id: The instance to enumerate
157  * @attr_name_kobj: The parent kernel object
158  */
populate_po_data(union acpi_object * po_obj,int instance_id,struct kobject * attr_name_kobj)159 int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj)
160 {
161 	wmi_priv.po_data[instance_id].attr_name_kobj = attr_name_kobj;
162 	if (check_property_type(po, ATTR_NAME, ACPI_TYPE_STRING))
163 		return -EINVAL;
164 	strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name,
165 		     po_obj[ATTR_NAME].string.pointer);
166 	if (check_property_type(po, MIN_PASS_LEN, ACPI_TYPE_INTEGER))
167 		return -EINVAL;
168 	wmi_priv.po_data[instance_id].min_password_length =
169 		(uintptr_t)po_obj[MIN_PASS_LEN].string.pointer;
170 	if (check_property_type(po, MAX_PASS_LEN, ACPI_TYPE_INTEGER))
171 		return -EINVAL;
172 	wmi_priv.po_data[instance_id].max_password_length =
173 		(uintptr_t) po_obj[MAX_PASS_LEN].string.pointer;
174 
175 	return sysfs_create_group(attr_name_kobj, &po_attr_group);
176 }
177 
178 /**
179  * exit_po_attributes() - Clear all attribute data
180  *
181  * Clears all data allocated for this group of attributes
182  */
exit_po_attributes(void)183 void exit_po_attributes(void)
184 {
185 	int instance_id;
186 
187 	for (instance_id = 0; instance_id < wmi_priv.po_instances_count; instance_id++) {
188 		if (wmi_priv.po_data[instance_id].attr_name_kobj)
189 			sysfs_remove_group(wmi_priv.po_data[instance_id].attr_name_kobj,
190 								&po_attr_group);
191 	}
192 	wmi_priv.po_instances_count = 0;
193 
194 	kfree(wmi_priv.po_data);
195 	wmi_priv.po_data = NULL;
196 }
197