Blame view

security/keys/persistent.c 4.34 KB
b4d0d230c   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
f36f8c75a   David Howells   KEYS: Add per-use...
2
3
4
5
  /* General persistent per-UID keyrings register
   *
   * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
f36f8c75a   David Howells   KEYS: Add per-use...
6
7
8
   */
  
  #include <linux/user_namespace.h>
5b825c3af   Ingo Molnar   sched/headers: Pr...
9
  #include <linux/cred.h>
f36f8c75a   David Howells   KEYS: Add per-use...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  #include "internal.h"
  
  unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
  
  /*
   * Create the persistent keyring register for the current user namespace.
   *
   * Called with the namespace's sem locked for writing.
   */
  static int key_create_persistent_register(struct user_namespace *ns)
  {
  	struct key *reg = keyring_alloc(".persistent_register",
  					KUIDT_INIT(0), KGIDT_INIT(0),
  					current_cred(),
028db3e29   Linus Torvalds   Revert "Merge tag...
24
25
  					((KEY_POS_ALL & ~KEY_POS_SETATTR) |
  					 KEY_USR_VIEW | KEY_USR_READ),
5ac7eace2   David Howells   KEYS: Add a facil...
26
  					KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
f36f8c75a   David Howells   KEYS: Add per-use...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  	if (IS_ERR(reg))
  		return PTR_ERR(reg);
  
  	ns->persistent_keyring_register = reg;
  	return 0;
  }
  
  /*
   * Create the persistent keyring for the specified user.
   *
   * Called with the namespace's sem locked for writing.
   */
  static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
  				       struct keyring_index_key *index_key)
  {
  	struct key *persistent;
  	key_ref_t reg_ref, persistent_ref;
  
  	if (!ns->persistent_keyring_register) {
  		long err = key_create_persistent_register(ns);
  		if (err < 0)
  			return ERR_PTR(err);
  	} else {
  		reg_ref = make_key_ref(ns->persistent_keyring_register, true);
  		persistent_ref = find_key_to_update(reg_ref, index_key);
  		if (persistent_ref)
  			return persistent_ref;
  	}
  
  	persistent = keyring_alloc(index_key->description,
  				   uid, INVALID_GID, current_cred(),
028db3e29   Linus Torvalds   Revert "Merge tag...
58
59
  				   ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
  				    KEY_USR_VIEW | KEY_USR_READ),
5ac7eace2   David Howells   KEYS: Add a facil...
60
  				   KEY_ALLOC_NOT_IN_QUOTA, NULL,
f36f8c75a   David Howells   KEYS: Add per-use...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  				   ns->persistent_keyring_register);
  	if (IS_ERR(persistent))
  		return ERR_CAST(persistent);
  
  	return make_key_ref(persistent, true);
  }
  
  /*
   * Get the persistent keyring for a specific UID and link it to the nominated
   * keyring.
   */
  static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
  			       key_ref_t dest_ref)
  {
  	struct keyring_index_key index_key;
  	struct key *persistent;
  	key_ref_t reg_ref, persistent_ref;
  	char buf[32];
  	long ret;
  
  	/* Look in the register if it exists */
3b6e4de05   David Howells   keys: Include tar...
82
  	memset(&index_key, 0, sizeof(index_key));
f36f8c75a   David Howells   KEYS: Add per-use...
83
84
85
  	index_key.type = &key_type_keyring;
  	index_key.description = buf;
  	index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
f771fde82   David Howells   keys: Simplify ke...
86
  	key_set_index_key(&index_key);
f36f8c75a   David Howells   KEYS: Add per-use...
87
88
89
  
  	if (ns->persistent_keyring_register) {
  		reg_ref = make_key_ref(ns->persistent_keyring_register, true);
0f44e4d97   David Howells   keys: Move the us...
90
  		down_read(&ns->keyring_sem);
f36f8c75a   David Howells   KEYS: Add per-use...
91
  		persistent_ref = find_key_to_update(reg_ref, &index_key);
0f44e4d97   David Howells   keys: Move the us...
92
  		up_read(&ns->keyring_sem);
f36f8c75a   David Howells   KEYS: Add per-use...
93
94
95
96
97
98
99
100
  
  		if (persistent_ref)
  			goto found;
  	}
  
  	/* It wasn't in the register, so we'll need to create it.  We might
  	 * also need to create the register.
  	 */
0f44e4d97   David Howells   keys: Move the us...
101
  	down_write(&ns->keyring_sem);
f36f8c75a   David Howells   KEYS: Add per-use...
102
  	persistent_ref = key_create_persistent(ns, uid, &index_key);
0f44e4d97   David Howells   keys: Move the us...
103
  	up_write(&ns->keyring_sem);
f36f8c75a   David Howells   KEYS: Add per-use...
104
105
106
107
108
109
  	if (!IS_ERR(persistent_ref))
  		goto found;
  
  	return PTR_ERR(persistent_ref);
  
  found:
f5895943d   David Howells   KEYS: Move the fl...
110
  	ret = key_task_permission(persistent_ref, current_cred(), KEY_NEED_LINK);
f36f8c75a   David Howells   KEYS: Add per-use...
111
112
113
114
115
  	if (ret == 0) {
  		persistent = key_ref_to_ptr(persistent_ref);
  		ret = key_link(key_ref_to_ptr(dest_ref), persistent);
  		if (ret == 0) {
  			key_set_timeout(persistent, persistent_keyring_expiry);
965475acc   David Howells   KEYS: Strip trail...
116
  			ret = persistent->serial;
f36f8c75a   David Howells   KEYS: Add per-use...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  		}
  	}
  
  	key_ref_put(persistent_ref);
  	return ret;
  }
  
  /*
   * Get the persistent keyring for a specific UID and link it to the nominated
   * keyring.
   */
  long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
  {
  	struct user_namespace *ns = current_user_ns();
  	key_ref_t dest_ref;
  	kuid_t uid;
  	long ret;
  
  	/* -1 indicates the current user */
  	if (_uid == (uid_t)-1) {
  		uid = current_uid();
  	} else {
  		uid = make_kuid(ns, _uid);
  		if (!uid_valid(uid))
  			return -EINVAL;
  
  		/* You can only see your own persistent cache if you're not
  		 * sufficiently privileged.
  		 */
fbf8c53f1   David Howells   KEYS: Fix UID che...
146
147
  		if (!uid_eq(uid, current_uid()) &&
  		    !uid_eq(uid, current_euid()) &&
f36f8c75a   David Howells   KEYS: Add per-use...
148
149
150
151
152
  		    !ns_capable(ns, CAP_SETUID))
  			return -EPERM;
  	}
  
  	/* There must be a destination keyring */
f5895943d   David Howells   KEYS: Move the fl...
153
  	dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
f36f8c75a   David Howells   KEYS: Add per-use...
154
155
156
157
158
159
160
161
162
163
164
165
166
  	if (IS_ERR(dest_ref))
  		return PTR_ERR(dest_ref);
  	if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
  		ret = -ENOTDIR;
  		goto out_put_dest;
  	}
  
  	ret = key_get_persistent(ns, uid, dest_ref);
  
  out_put_dest:
  	key_ref_put(dest_ref);
  	return ret;
  }