aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc
diff options
context:
space:
mode:
authorAndy Adamson <andros@netapp.com>2013-08-14 11:59:15 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2013-09-03 15:25:08 -0400
commit4de6caa270afaa381dd3373e9e6d148b1090e0ec (patch)
tree2b212167c872f2038ed3480df872766bbd7b67e3 /net/sunrpc
parentf1ff0c27fd9987c59d707cd1a6b6c1fc3ae0a250 (diff)
SUNRPC new rpc_credops to test credential expiry
This patch provides the RPC layer helper functions to allow NFS to manage data in the face of expired credentials - such as avoiding buffered WRITEs and COMMITs when the gss context will expire before the WRITEs are flushed and COMMITs are sent. These helper functions enable checking the expiration of an underlying credential key for a generic rpc credential, e.g. the gss_cred gss context gc_expiry which for Kerberos is set to the remaining TGT lifetime. A new rpc_authops key_timeout is only defined for the generic auth. A new rpc_credops crkey_to_expire is only defined for the generic cred. A new rpc_credops crkey_timeout is only defined for the gss cred. Set a credential key expiry watermark, RPC_KEY_EXPIRE_TIMEO set to 240 seconds as a default and can be set via a module parameter as we need to ensure there is time for any dirty data to be flushed. If key_timeout is called on a credential with an underlying credential key that will expire within watermark seconds, we set the RPC_CRED_KEY_EXPIRE_SOON flag in the generic_cred acred so that the NFS layer can clean up prior to key expiration. Checking a generic credential's underlying credential involves a cred lookup. To avoid this lookup in the normal case when the underlying credential has a key that is valid (before the watermark), a notify flag is set in the generic credential the first time the key_timeout is called. The generic credential then stops checking the underlying credential key expiry, and the underlying credential (gss_cred) match routine then checks the key expiration upon each normal use and sets a flag in the associated generic credential only when the key expiration is within the watermark. This in turn signals the generic credential key_timeout to perform the extra credential lookup thereafter. Signed-off-by: Andy Adamson <andros@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc')
-rw-r--r--net/sunrpc/auth.c21
-rw-r--r--net/sunrpc/auth_generic.c82
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c52
3 files changed, 153 insertions, 2 deletions
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 5071e435471c..415159061cd0 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -343,6 +343,27 @@ out_nocache:
343EXPORT_SYMBOL_GPL(rpcauth_init_credcache); 343EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
344 344
345/* 345/*
346 * Setup a credential key lifetime timeout notification
347 */
348int
349rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred)
350{
351 if (!cred->cr_auth->au_ops->key_timeout)
352 return 0;
353 return cred->cr_auth->au_ops->key_timeout(auth, cred);
354}
355EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify);
356
357bool
358rpcauth_cred_key_to_expire(struct rpc_cred *cred)
359{
360 if (!cred->cr_ops->crkey_to_expire)
361 return false;
362 return cred->cr_ops->crkey_to_expire(cred);
363}
364EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire);
365
366/*
346 * Destroy a list of credentials 367 * Destroy a list of credentials
347 */ 368 */
348static inline 369static inline
diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c
index b6badafc6494..f6d84be49050 100644
--- a/net/sunrpc/auth_generic.c
+++ b/net/sunrpc/auth_generic.c
@@ -89,6 +89,7 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
89 gcred->acred.uid = acred->uid; 89 gcred->acred.uid = acred->uid;
90 gcred->acred.gid = acred->gid; 90 gcred->acred.gid = acred->gid;
91 gcred->acred.group_info = acred->group_info; 91 gcred->acred.group_info = acred->group_info;
92 gcred->acred.ac_flags = 0;
92 if (gcred->acred.group_info != NULL) 93 if (gcred->acred.group_info != NULL)
93 get_group_info(gcred->acred.group_info); 94 get_group_info(gcred->acred.group_info);
94 gcred->acred.machine_cred = acred->machine_cred; 95 gcred->acred.machine_cred = acred->machine_cred;
@@ -182,11 +183,78 @@ void rpc_destroy_generic_auth(void)
182 rpcauth_destroy_credcache(&generic_auth); 183 rpcauth_destroy_credcache(&generic_auth);
183} 184}
184 185
186/*
187 * Test the the current time (now) against the underlying credential key expiry
188 * minus a timeout and setup notification.
189 *
190 * The normal case:
191 * If 'now' is before the key expiry minus RPC_KEY_EXPIRE_TIMEO, set
192 * the RPC_CRED_NOTIFY_TIMEOUT flag to setup the underlying credential
193 * rpc_credops crmatch routine to notify this generic cred when it's key
194 * expiration is within RPC_KEY_EXPIRE_TIMEO, and return 0.
195 *
196 * The error case:
197 * If the underlying cred lookup fails, return -EACCES.
198 *
199 * The 'almost' error case:
200 * If 'now' is within key expiry minus RPC_KEY_EXPIRE_TIMEO, but not within
201 * key expiry minus RPC_KEY_EXPIRE_FAIL, set the RPC_CRED_EXPIRE_SOON bit
202 * on the acred ac_flags and return 0.
203 */
204static int
205generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
206{
207 struct auth_cred *acred = &container_of(cred, struct generic_cred,
208 gc_base)->acred;
209 struct rpc_cred *tcred;
210 int ret = 0;
211
212
213 /* Fast track for non crkey_timeout (no key) underlying credentials */
214 if (test_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags))
215 return 0;
216
217 /* Fast track for the normal case */
218 if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags))
219 return 0;
220
221 /* lookup_cred either returns a valid referenced rpc_cred, or PTR_ERR */
222 tcred = auth->au_ops->lookup_cred(auth, acred, 0);
223 if (IS_ERR(tcred))
224 return -EACCES;
225
226 if (!tcred->cr_ops->crkey_timeout) {
227 set_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags);
228 ret = 0;
229 goto out_put;
230 }
231
232 /* Test for the almost error case */
233 ret = tcred->cr_ops->crkey_timeout(tcred);
234 if (ret != 0) {
235 set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
236 ret = 0;
237 } else {
238 /* In case underlying cred key has been reset */
239 if (test_and_clear_bit(RPC_CRED_KEY_EXPIRE_SOON,
240 &acred->ac_flags))
241 dprintk("RPC: UID %d Credential key reset\n",
242 tcred->cr_uid);
243 /* set up fasttrack for the normal case */
244 set_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
245 }
246
247out_put:
248 put_rpccred(tcred);
249 return ret;
250}
251
185static const struct rpc_authops generic_auth_ops = { 252static const struct rpc_authops generic_auth_ops = {
186 .owner = THIS_MODULE, 253 .owner = THIS_MODULE,
187 .au_name = "Generic", 254 .au_name = "Generic",
188 .lookup_cred = generic_lookup_cred, 255 .lookup_cred = generic_lookup_cred,
189 .crcreate = generic_create_cred, 256 .crcreate = generic_create_cred,
257 .key_timeout = generic_key_timeout,
190}; 258};
191 259
192static struct rpc_auth generic_auth = { 260static struct rpc_auth generic_auth = {
@@ -194,9 +262,23 @@ static struct rpc_auth generic_auth = {
194 .au_count = ATOMIC_INIT(0), 262 .au_count = ATOMIC_INIT(0),
195}; 263};
196 264
265static bool generic_key_to_expire(struct rpc_cred *cred)
266{
267 struct auth_cred *acred = &container_of(cred, struct generic_cred,
268 gc_base)->acred;
269 bool ret;
270
271 get_rpccred(cred);
272 ret = test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
273 put_rpccred(cred);
274
275 return ret;
276}
277
197static const struct rpc_credops generic_credops = { 278static const struct rpc_credops generic_credops = {
198 .cr_name = "Generic cred", 279 .cr_name = "Generic cred",
199 .crdestroy = generic_destroy_cred, 280 .crdestroy = generic_destroy_cred,
200 .crbind = generic_bind_cred, 281 .crbind = generic_bind_cred,
201 .crmatch = generic_match, 282 .crmatch = generic_match,
283 .crkey_to_expire = generic_key_to_expire,
202}; 284};
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index dc4b449d3680..a9b1edca917c 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -63,6 +63,9 @@ static const struct rpc_credops gss_nullops;
63#define GSS_RETRY_EXPIRED 5 63#define GSS_RETRY_EXPIRED 5
64static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; 64static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
65 65
66#define GSS_KEY_EXPIRE_TIMEO 240
67static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
68
66#ifdef RPC_DEBUG 69#ifdef RPC_DEBUG
67# define RPCDBG_FACILITY RPCDBG_AUTH 70# define RPCDBG_FACILITY RPCDBG_AUTH
68#endif 71#endif
@@ -1295,10 +1298,32 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)
1295 return err; 1298 return err;
1296} 1299}
1297 1300
1301/*
1302 * Returns -EACCES if GSS context is NULL or will expire within the
1303 * timeout (miliseconds)
1304 */
1305static int
1306gss_key_timeout(struct rpc_cred *rc)
1307{
1308 struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
1309 unsigned long now = jiffies;
1310 unsigned long expire;
1311
1312 if (gss_cred->gc_ctx == NULL)
1313 return -EACCES;
1314
1315 expire = gss_cred->gc_ctx->gc_expiry - (gss_key_expire_timeo * HZ);
1316
1317 if (time_after(now, expire))
1318 return -EACCES;
1319 return 0;
1320}
1321
1298static int 1322static int
1299gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) 1323gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
1300{ 1324{
1301 struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 1325 struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
1326 int ret;
1302 1327
1303 if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) 1328 if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
1304 goto out; 1329 goto out;
@@ -1311,11 +1336,26 @@ out:
1311 if (acred->principal != NULL) { 1336 if (acred->principal != NULL) {
1312 if (gss_cred->gc_principal == NULL) 1337 if (gss_cred->gc_principal == NULL)
1313 return 0; 1338 return 0;
1314 return strcmp(acred->principal, gss_cred->gc_principal) == 0; 1339 ret = strcmp(acred->principal, gss_cred->gc_principal) == 0;
1340 goto check_expire;
1315 } 1341 }
1316 if (gss_cred->gc_principal != NULL) 1342 if (gss_cred->gc_principal != NULL)
1317 return 0; 1343 return 0;
1318 return uid_eq(rc->cr_uid, acred->uid); 1344 ret = uid_eq(rc->cr_uid, acred->uid);
1345
1346check_expire:
1347 if (ret == 0)
1348 return ret;
1349
1350 /* Notify acred users of GSS context expiration timeout */
1351 if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags) &&
1352 (gss_key_timeout(rc) != 0)) {
1353 /* test will now be done from generic cred */
1354 test_and_clear_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags);
1355 /* tell NFS layer that key will expire soon */
1356 set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags);
1357 }
1358 return ret;
1319} 1359}
1320 1360
1321/* 1361/*
@@ -1842,6 +1882,7 @@ static const struct rpc_credops gss_credops = {
1842 .crvalidate = gss_validate, 1882 .crvalidate = gss_validate,
1843 .crwrap_req = gss_wrap_req, 1883 .crwrap_req = gss_wrap_req,
1844 .crunwrap_resp = gss_unwrap_resp, 1884 .crunwrap_resp = gss_unwrap_resp,
1885 .crkey_timeout = gss_key_timeout,
1845}; 1886};
1846 1887
1847static const struct rpc_credops gss_nullops = { 1888static const struct rpc_credops gss_nullops = {
@@ -1929,5 +1970,12 @@ module_param_named(expired_cred_retry_delay,
1929MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " 1970MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
1930 "the RPC engine retries an expired credential"); 1971 "the RPC engine retries an expired credential");
1931 1972
1973module_param_named(key_expire_timeo,
1974 gss_key_expire_timeo,
1975 uint, 0644);
1976MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a "
1977 "credential keys lifetime where the NFS layer cleans up "
1978 "prior to key expiration");
1979
1932module_init(init_rpcsec_gss) 1980module_init(init_rpcsec_gss)
1933module_exit(exit_rpcsec_gss) 1981module_exit(exit_rpcsec_gss)