diff options
Diffstat (limited to 'net/sunrpc/auth_gss/auth_gss.c')
| -rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 89 |
1 files changed, 71 insertions, 18 deletions
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index c389ccf6437d..8da2a0e68574 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
| @@ -57,11 +57,14 @@ static const struct rpc_authops authgss_ops; | |||
| 57 | static const struct rpc_credops gss_credops; | 57 | static const struct rpc_credops gss_credops; |
| 58 | static const struct rpc_credops gss_nullops; | 58 | static const struct rpc_credops gss_nullops; |
| 59 | 59 | ||
| 60 | #define GSS_RETRY_EXPIRED 5 | ||
| 61 | static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; | ||
| 62 | |||
| 60 | #ifdef RPC_DEBUG | 63 | #ifdef RPC_DEBUG |
| 61 | # define RPCDBG_FACILITY RPCDBG_AUTH | 64 | # define RPCDBG_FACILITY RPCDBG_AUTH |
| 62 | #endif | 65 | #endif |
| 63 | 66 | ||
| 64 | #define GSS_CRED_SLACK 1024 | 67 | #define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2) |
| 65 | /* length of a krb5 verifier (48), plus data added before arguments when | 68 | /* length of a krb5 verifier (48), plus data added before arguments when |
| 66 | * using integrity (two 4-byte integers): */ | 69 | * using integrity (two 4-byte integers): */ |
| 67 | #define GSS_VERF_SLACK 100 | 70 | #define GSS_VERF_SLACK 100 |
| @@ -229,7 +232,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct | |||
| 229 | p = ERR_PTR(-EFAULT); | 232 | p = ERR_PTR(-EFAULT); |
| 230 | goto err; | 233 | goto err; |
| 231 | } | 234 | } |
| 232 | ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx); | 235 | ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, GFP_NOFS); |
| 233 | if (ret < 0) { | 236 | if (ret < 0) { |
| 234 | p = ERR_PTR(ret); | 237 | p = ERR_PTR(ret); |
| 235 | goto err; | 238 | goto err; |
| @@ -350,6 +353,24 @@ gss_unhash_msg(struct gss_upcall_msg *gss_msg) | |||
| 350 | } | 353 | } |
| 351 | 354 | ||
| 352 | static void | 355 | static void |
| 356 | gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg) | ||
| 357 | { | ||
| 358 | switch (gss_msg->msg.errno) { | ||
| 359 | case 0: | ||
| 360 | if (gss_msg->ctx == NULL) | ||
| 361 | break; | ||
| 362 | clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); | ||
| 363 | gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx); | ||
| 364 | break; | ||
| 365 | case -EKEYEXPIRED: | ||
| 366 | set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); | ||
| 367 | } | ||
| 368 | gss_cred->gc_upcall_timestamp = jiffies; | ||
| 369 | gss_cred->gc_upcall = NULL; | ||
| 370 | rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); | ||
| 371 | } | ||
| 372 | |||
| 373 | static void | ||
| 353 | gss_upcall_callback(struct rpc_task *task) | 374 | gss_upcall_callback(struct rpc_task *task) |
| 354 | { | 375 | { |
| 355 | struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, | 376 | struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, |
| @@ -358,13 +379,9 @@ gss_upcall_callback(struct rpc_task *task) | |||
| 358 | struct inode *inode = &gss_msg->inode->vfs_inode; | 379 | struct inode *inode = &gss_msg->inode->vfs_inode; |
| 359 | 380 | ||
| 360 | spin_lock(&inode->i_lock); | 381 | spin_lock(&inode->i_lock); |
| 361 | if (gss_msg->ctx) | 382 | gss_handle_downcall_result(gss_cred, gss_msg); |
| 362 | gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx); | ||
| 363 | else | ||
| 364 | task->tk_status = gss_msg->msg.errno; | ||
| 365 | gss_cred->gc_upcall = NULL; | ||
| 366 | rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); | ||
| 367 | spin_unlock(&inode->i_lock); | 383 | spin_unlock(&inode->i_lock); |
| 384 | task->tk_status = gss_msg->msg.errno; | ||
| 368 | gss_release_msg(gss_msg); | 385 | gss_release_msg(gss_msg); |
| 369 | } | 386 | } |
| 370 | 387 | ||
| @@ -377,11 +394,12 @@ static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg) | |||
| 377 | static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, | 394 | static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, |
| 378 | struct rpc_clnt *clnt, int machine_cred) | 395 | struct rpc_clnt *clnt, int machine_cred) |
| 379 | { | 396 | { |
| 397 | struct gss_api_mech *mech = gss_msg->auth->mech; | ||
| 380 | char *p = gss_msg->databuf; | 398 | char *p = gss_msg->databuf; |
| 381 | int len = 0; | 399 | int len = 0; |
| 382 | 400 | ||
| 383 | gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ", | 401 | gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ", |
| 384 | gss_msg->auth->mech->gm_name, | 402 | mech->gm_name, |
| 385 | gss_msg->uid); | 403 | gss_msg->uid); |
| 386 | p += gss_msg->msg.len; | 404 | p += gss_msg->msg.len; |
| 387 | if (clnt->cl_principal) { | 405 | if (clnt->cl_principal) { |
| @@ -398,6 +416,11 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, | |||
| 398 | p += len; | 416 | p += len; |
| 399 | gss_msg->msg.len += len; | 417 | gss_msg->msg.len += len; |
| 400 | } | 418 | } |
| 419 | if (mech->gm_upcall_enctypes) { | ||
| 420 | len = sprintf(p, mech->gm_upcall_enctypes); | ||
| 421 | p += len; | ||
| 422 | gss_msg->msg.len += len; | ||
| 423 | } | ||
| 401 | len = sprintf(p, "\n"); | 424 | len = sprintf(p, "\n"); |
| 402 | gss_msg->msg.len += len; | 425 | gss_msg->msg.len += len; |
| 403 | 426 | ||
| @@ -507,18 +530,16 @@ gss_refresh_upcall(struct rpc_task *task) | |||
| 507 | spin_lock(&inode->i_lock); | 530 | spin_lock(&inode->i_lock); |
| 508 | if (gss_cred->gc_upcall != NULL) | 531 | if (gss_cred->gc_upcall != NULL) |
| 509 | rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); | 532 | rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); |
| 510 | else if (gss_msg->ctx != NULL) { | 533 | else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { |
| 511 | gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_msg->ctx); | ||
| 512 | gss_cred->gc_upcall = NULL; | ||
| 513 | rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); | ||
| 514 | } else if (gss_msg->msg.errno >= 0) { | ||
| 515 | task->tk_timeout = 0; | 534 | task->tk_timeout = 0; |
| 516 | gss_cred->gc_upcall = gss_msg; | 535 | gss_cred->gc_upcall = gss_msg; |
| 517 | /* gss_upcall_callback will release the reference to gss_upcall_msg */ | 536 | /* gss_upcall_callback will release the reference to gss_upcall_msg */ |
| 518 | atomic_inc(&gss_msg->count); | 537 | atomic_inc(&gss_msg->count); |
| 519 | rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback); | 538 | rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback); |
| 520 | } else | 539 | } else { |
| 540 | gss_handle_downcall_result(gss_cred, gss_msg); | ||
| 521 | err = gss_msg->msg.errno; | 541 | err = gss_msg->msg.errno; |
| 542 | } | ||
| 522 | spin_unlock(&inode->i_lock); | 543 | spin_unlock(&inode->i_lock); |
| 523 | gss_release_msg(gss_msg); | 544 | gss_release_msg(gss_msg); |
| 524 | out: | 545 | out: |
| @@ -1117,6 +1138,23 @@ static int gss_renew_cred(struct rpc_task *task) | |||
| 1117 | return 0; | 1138 | return 0; |
| 1118 | } | 1139 | } |
| 1119 | 1140 | ||
| 1141 | static int gss_cred_is_negative_entry(struct rpc_cred *cred) | ||
| 1142 | { | ||
| 1143 | if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) { | ||
| 1144 | unsigned long now = jiffies; | ||
| 1145 | unsigned long begin, expire; | ||
| 1146 | struct gss_cred *gss_cred; | ||
| 1147 | |||
| 1148 | gss_cred = container_of(cred, struct gss_cred, gc_base); | ||
| 1149 | begin = gss_cred->gc_upcall_timestamp; | ||
| 1150 | expire = begin + gss_expired_cred_retry_delay * HZ; | ||
| 1151 | |||
| 1152 | if (time_in_range_open(now, begin, expire)) | ||
| 1153 | return 1; | ||
| 1154 | } | ||
| 1155 | return 0; | ||
| 1156 | } | ||
| 1157 | |||
| 1120 | /* | 1158 | /* |
| 1121 | * Refresh credentials. XXX - finish | 1159 | * Refresh credentials. XXX - finish |
| 1122 | */ | 1160 | */ |
| @@ -1126,6 +1164,9 @@ gss_refresh(struct rpc_task *task) | |||
| 1126 | struct rpc_cred *cred = task->tk_msg.rpc_cred; | 1164 | struct rpc_cred *cred = task->tk_msg.rpc_cred; |
| 1127 | int ret = 0; | 1165 | int ret = 0; |
| 1128 | 1166 | ||
| 1167 | if (gss_cred_is_negative_entry(cred)) | ||
| 1168 | return -EKEYEXPIRED; | ||
| 1169 | |||
| 1129 | if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && | 1170 | if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && |
| 1130 | !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) { | 1171 | !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) { |
| 1131 | ret = gss_renew_cred(task); | 1172 | ret = gss_renew_cred(task); |
| @@ -1316,15 +1357,21 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |||
| 1316 | inpages = snd_buf->pages + first; | 1357 | inpages = snd_buf->pages + first; |
| 1317 | snd_buf->pages = rqstp->rq_enc_pages; | 1358 | snd_buf->pages = rqstp->rq_enc_pages; |
| 1318 | snd_buf->page_base -= first << PAGE_CACHE_SHIFT; | 1359 | snd_buf->page_base -= first << PAGE_CACHE_SHIFT; |
| 1319 | /* Give the tail its own page, in case we need extra space in the | 1360 | /* |
| 1320 | * head when wrapping: */ | 1361 | * Give the tail its own page, in case we need extra space in the |
| 1362 | * head when wrapping: | ||
| 1363 | * | ||
| 1364 | * call_allocate() allocates twice the slack space required | ||
| 1365 | * by the authentication flavor to rq_callsize. | ||
| 1366 | * For GSS, slack is GSS_CRED_SLACK. | ||
| 1367 | */ | ||
| 1321 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) { | 1368 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) { |
| 1322 | tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); | 1369 | tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); |
| 1323 | memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); | 1370 | memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); |
| 1324 | snd_buf->tail[0].iov_base = tmp; | 1371 | snd_buf->tail[0].iov_base = tmp; |
| 1325 | } | 1372 | } |
| 1326 | maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); | 1373 | maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); |
| 1327 | /* RPC_SLACK_SPACE should prevent this ever happening: */ | 1374 | /* slack space should prevent this ever happening: */ |
| 1328 | BUG_ON(snd_buf->len > snd_buf->buflen); | 1375 | BUG_ON(snd_buf->len > snd_buf->buflen); |
| 1329 | status = -EIO; | 1376 | status = -EIO; |
| 1330 | /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was | 1377 | /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was |
| @@ -1573,5 +1620,11 @@ static void __exit exit_rpcsec_gss(void) | |||
| 1573 | } | 1620 | } |
| 1574 | 1621 | ||
| 1575 | MODULE_LICENSE("GPL"); | 1622 | MODULE_LICENSE("GPL"); |
| 1623 | module_param_named(expired_cred_retry_delay, | ||
| 1624 | gss_expired_cred_retry_delay, | ||
| 1625 | uint, 0644); | ||
| 1626 | MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " | ||
| 1627 | "the RPC engine retries an expired credential"); | ||
| 1628 | |||
| 1576 | module_init(init_rpcsec_gss) | 1629 | module_init(init_rpcsec_gss) |
| 1577 | module_exit(exit_rpcsec_gss) | 1630 | module_exit(exit_rpcsec_gss) |
