- patches.arch/x86_mce_intel_decode_physical_address.patch:
[linux-flexiantxendom0-3.2.10.git] / net / sunrpc / auth_gss / auth_gss.c
index c389ccf..8da2a0e 100644 (file)
@@ -57,11 +57,14 @@ static const struct rpc_authops authgss_ops;
 static const struct rpc_credops gss_credops;
 static const struct rpc_credops gss_nullops;
 
+#define GSS_RETRY_EXPIRED 5
+static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
+
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY       RPCDBG_AUTH
 #endif
 
-#define GSS_CRED_SLACK         1024
+#define GSS_CRED_SLACK         (RPC_MAX_AUTH_SIZE * 2)
 /* length of a krb5 verifier (48), plus data added before arguments when
  * using integrity (two 4-byte integers): */
 #define GSS_VERF_SLACK         100
@@ -229,7 +232,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct
                p = ERR_PTR(-EFAULT);
                goto err;
        }
-       ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx);
+       ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, GFP_NOFS);
        if (ret < 0) {
                p = ERR_PTR(ret);
                goto err;
@@ -350,6 +353,24 @@ gss_unhash_msg(struct gss_upcall_msg *gss_msg)
 }
 
 static void
+gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg)
+{
+       switch (gss_msg->msg.errno) {
+       case 0:
+               if (gss_msg->ctx == NULL)
+                       break;
+               clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
+               gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx);
+               break;
+       case -EKEYEXPIRED:
+               set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags);
+       }
+       gss_cred->gc_upcall_timestamp = jiffies;
+       gss_cred->gc_upcall = NULL;
+       rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
+}
+
+static void
 gss_upcall_callback(struct rpc_task *task)
 {
        struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred,
@@ -358,13 +379,9 @@ gss_upcall_callback(struct rpc_task *task)
        struct inode *inode = &gss_msg->inode->vfs_inode;
 
        spin_lock(&inode->i_lock);
-       if (gss_msg->ctx)
-               gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx);
-       else
-               task->tk_status = gss_msg->msg.errno;
-       gss_cred->gc_upcall = NULL;
-       rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
+       gss_handle_downcall_result(gss_cred, gss_msg);
        spin_unlock(&inode->i_lock);
+       task->tk_status = gss_msg->msg.errno;
        gss_release_msg(gss_msg);
 }
 
@@ -377,11 +394,12 @@ static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)
 static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
                                struct rpc_clnt *clnt, int machine_cred)
 {
+       struct gss_api_mech *mech = gss_msg->auth->mech;
        char *p = gss_msg->databuf;
        int len = 0;
 
        gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ",
-                                  gss_msg->auth->mech->gm_name,
+                                  mech->gm_name,
                                   gss_msg->uid);
        p += gss_msg->msg.len;
        if (clnt->cl_principal) {
@@ -398,6 +416,11 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
                p += len;
                gss_msg->msg.len += len;
        }
+       if (mech->gm_upcall_enctypes) {
+               len = sprintf(p, mech->gm_upcall_enctypes);
+               p += len;
+               gss_msg->msg.len += len;
+       }
        len = sprintf(p, "\n");
        gss_msg->msg.len += len;
 
@@ -507,18 +530,16 @@ gss_refresh_upcall(struct rpc_task *task)
        spin_lock(&inode->i_lock);
        if (gss_cred->gc_upcall != NULL)
                rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
-       else if (gss_msg->ctx != NULL) {
-               gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx);
-               gss_cred->gc_upcall = NULL;
-               rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
-       } else if (gss_msg->msg.errno >= 0) {
+       else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
                task->tk_timeout = 0;
                gss_cred->gc_upcall = gss_msg;
                /* gss_upcall_callback will release the reference to gss_upcall_msg */
                atomic_inc(&gss_msg->count);
                rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback);
-       } else
+       } else {
+               gss_handle_downcall_result(gss_cred, gss_msg);
                err = gss_msg->msg.errno;
+       }
        spin_unlock(&inode->i_lock);
        gss_release_msg(gss_msg);
 out:
@@ -1117,6 +1138,23 @@ static int gss_renew_cred(struct rpc_task *task)
        return 0;
 }
 
+static int gss_cred_is_negative_entry(struct rpc_cred *cred)
+{
+       if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) {
+               unsigned long now = jiffies;
+               unsigned long begin, expire;
+               struct gss_cred *gss_cred; 
+
+               gss_cred = container_of(cred, struct gss_cred, gc_base);
+               begin = gss_cred->gc_upcall_timestamp;
+               expire = begin + gss_expired_cred_retry_delay * HZ;
+
+               if (time_in_range_open(now, begin, expire))
+                       return 1;
+       }
+       return 0;
+}
+
 /*
 * Refresh credentials. XXX - finish
 */
@@ -1126,6 +1164,9 @@ gss_refresh(struct rpc_task *task)
        struct rpc_cred *cred = task->tk_msg.rpc_cred;
        int ret = 0;
 
+       if (gss_cred_is_negative_entry(cred))
+               return -EKEYEXPIRED;
+
        if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
                        !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) {
                ret = gss_renew_cred(task);
@@ -1316,15 +1357,21 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        inpages = snd_buf->pages + first;
        snd_buf->pages = rqstp->rq_enc_pages;
        snd_buf->page_base -= first << PAGE_CACHE_SHIFT;
-       /* Give the tail its own page, in case we need extra space in the
-        * head when wrapping: */
+       /*
+        * Give the tail its own page, in case we need extra space in the
+        * head when wrapping:
+        *
+        * call_allocate() allocates twice the slack space required
+        * by the authentication flavor to rq_callsize.
+        * For GSS, slack is GSS_CRED_SLACK.
+        */
        if (snd_buf->page_len || snd_buf->tail[0].iov_len) {
                tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]);
                memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len);
                snd_buf->tail[0].iov_base = tmp;
        }
        maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages);
-       /* RPC_SLACK_SPACE should prevent this ever happening: */
+       /* slack space should prevent this ever happening: */
        BUG_ON(snd_buf->len > snd_buf->buflen);
        status = -EIO;
        /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
@@ -1573,5 +1620,11 @@ static void __exit exit_rpcsec_gss(void)
 }
 
 MODULE_LICENSE("GPL");
+module_param_named(expired_cred_retry_delay,
+                  gss_expired_cred_retry_delay,
+                  uint, 0644);
+MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "
+               "the RPC engine retries an expired credential");
+
 module_init(init_rpcsec_gss)
 module_exit(exit_rpcsec_gss)