xref: /openbmc/linux/security/keys/user_defined.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1  // SPDX-License-Identifier: GPL-2.0-or-later
2  /* user_defined.c: user defined key type
3   *
4   * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
5   * Written by David Howells (dhowells@redhat.com)
6   */
7  
8  #include <linux/export.h>
9  #include <linux/init.h>
10  #include <linux/slab.h>
11  #include <linux/seq_file.h>
12  #include <linux/err.h>
13  #include <keys/user-type.h>
14  #include <linux/uaccess.h>
15  #include "internal.h"
16  
17  static int logon_vet_description(const char *desc);
18  
19  /*
20   * user defined keys take an arbitrary string as the description and an
21   * arbitrary blob of data as the payload
22   */
23  struct key_type key_type_user = {
24  	.name			= "user",
25  	.preparse		= user_preparse,
26  	.free_preparse		= user_free_preparse,
27  	.instantiate		= generic_key_instantiate,
28  	.update			= user_update,
29  	.revoke			= user_revoke,
30  	.destroy		= user_destroy,
31  	.describe		= user_describe,
32  	.read			= user_read,
33  };
34  
35  EXPORT_SYMBOL_GPL(key_type_user);
36  
37  /*
38   * This key type is essentially the same as key_type_user, but it does
39   * not define a .read op. This is suitable for storing username and
40   * password pairs in the keyring that you do not want to be readable
41   * from userspace.
42   */
43  struct key_type key_type_logon = {
44  	.name			= "logon",
45  	.preparse		= user_preparse,
46  	.free_preparse		= user_free_preparse,
47  	.instantiate		= generic_key_instantiate,
48  	.update			= user_update,
49  	.revoke			= user_revoke,
50  	.destroy		= user_destroy,
51  	.describe		= user_describe,
52  	.vet_description	= logon_vet_description,
53  };
54  EXPORT_SYMBOL_GPL(key_type_logon);
55  
56  /*
57   * Preparse a user defined key payload
58   */
user_preparse(struct key_preparsed_payload * prep)59  int user_preparse(struct key_preparsed_payload *prep)
60  {
61  	struct user_key_payload *upayload;
62  	size_t datalen = prep->datalen;
63  
64  	if (datalen <= 0 || datalen > 32767 || !prep->data)
65  		return -EINVAL;
66  
67  	upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
68  	if (!upayload)
69  		return -ENOMEM;
70  
71  	/* attach the data */
72  	prep->quotalen = datalen;
73  	prep->payload.data[0] = upayload;
74  	upayload->datalen = datalen;
75  	memcpy(upayload->data, prep->data, datalen);
76  	return 0;
77  }
78  EXPORT_SYMBOL_GPL(user_preparse);
79  
80  /*
81   * Free a preparse of a user defined key payload
82   */
user_free_preparse(struct key_preparsed_payload * prep)83  void user_free_preparse(struct key_preparsed_payload *prep)
84  {
85  	kfree_sensitive(prep->payload.data[0]);
86  }
87  EXPORT_SYMBOL_GPL(user_free_preparse);
88  
user_free_payload_rcu(struct rcu_head * head)89  static void user_free_payload_rcu(struct rcu_head *head)
90  {
91  	struct user_key_payload *payload;
92  
93  	payload = container_of(head, struct user_key_payload, rcu);
94  	kfree_sensitive(payload);
95  }
96  
97  /*
98   * update a user defined key
99   * - the key's semaphore is write-locked
100   */
user_update(struct key * key,struct key_preparsed_payload * prep)101  int user_update(struct key *key, struct key_preparsed_payload *prep)
102  {
103  	struct user_key_payload *zap = NULL;
104  	int ret;
105  
106  	/* check the quota and attach the new data */
107  	ret = key_payload_reserve(key, prep->datalen);
108  	if (ret < 0)
109  		return ret;
110  
111  	/* attach the new data, displacing the old */
112  	key->expiry = prep->expiry;
113  	if (key_is_positive(key))
114  		zap = dereference_key_locked(key);
115  	rcu_assign_keypointer(key, prep->payload.data[0]);
116  	prep->payload.data[0] = NULL;
117  
118  	if (zap)
119  		call_rcu(&zap->rcu, user_free_payload_rcu);
120  	return ret;
121  }
122  EXPORT_SYMBOL_GPL(user_update);
123  
124  /*
125   * dispose of the links from a revoked keyring
126   * - called with the key sem write-locked
127   */
user_revoke(struct key * key)128  void user_revoke(struct key *key)
129  {
130  	struct user_key_payload *upayload = user_key_payload_locked(key);
131  
132  	/* clear the quota */
133  	key_payload_reserve(key, 0);
134  
135  	if (upayload) {
136  		rcu_assign_keypointer(key, NULL);
137  		call_rcu(&upayload->rcu, user_free_payload_rcu);
138  	}
139  }
140  
141  EXPORT_SYMBOL(user_revoke);
142  
143  /*
144   * dispose of the data dangling from the corpse of a user key
145   */
user_destroy(struct key * key)146  void user_destroy(struct key *key)
147  {
148  	struct user_key_payload *upayload = key->payload.data[0];
149  
150  	kfree_sensitive(upayload);
151  }
152  
153  EXPORT_SYMBOL_GPL(user_destroy);
154  
155  /*
156   * describe the user key
157   */
user_describe(const struct key * key,struct seq_file * m)158  void user_describe(const struct key *key, struct seq_file *m)
159  {
160  	seq_puts(m, key->description);
161  	if (key_is_positive(key))
162  		seq_printf(m, ": %u", key->datalen);
163  }
164  
165  EXPORT_SYMBOL_GPL(user_describe);
166  
167  /*
168   * read the key data
169   * - the key's semaphore is read-locked
170   */
user_read(const struct key * key,char * buffer,size_t buflen)171  long user_read(const struct key *key, char *buffer, size_t buflen)
172  {
173  	const struct user_key_payload *upayload;
174  	long ret;
175  
176  	upayload = user_key_payload_locked(key);
177  	ret = upayload->datalen;
178  
179  	/* we can return the data as is */
180  	if (buffer && buflen > 0) {
181  		if (buflen > upayload->datalen)
182  			buflen = upayload->datalen;
183  
184  		memcpy(buffer, upayload->data, buflen);
185  	}
186  
187  	return ret;
188  }
189  
190  EXPORT_SYMBOL_GPL(user_read);
191  
192  /* Vet the description for a "logon" key */
logon_vet_description(const char * desc)193  static int logon_vet_description(const char *desc)
194  {
195  	char *p;
196  
197  	/* require a "qualified" description string */
198  	p = strchr(desc, ':');
199  	if (!p)
200  		return -EINVAL;
201  
202  	/* also reject description with ':' as first char */
203  	if (p == desc)
204  		return -EINVAL;
205  
206  	return 0;
207  }
208