From c570ba23838c080deecb30b0363a636947745df4 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Thu, 30 Jun 2011 12:21:07 -0400 Subject: [PATCH] first stab at SECINFO handling receiving WRONGSEC error in compound_encode_decode function by sending either SECINFO or SECINFO_NONAME op to find out available security flavors from the server. then try to establish new security context given the ordered list returned by the server. Not handling if parent directory doesn't permit a security flavor of its child directory. Example "/" exported with only auth_sys and "/sec" exported with only "gss". --- daemon/nfs41_compound.c | 171 +++++++++++++++++++++++++++++++++++++++- daemon/nfs41_ops.c | 24 ++++-- daemon/nfs41_ops.h | 1 + 3 files changed, 189 insertions(+), 7 deletions(-) diff --git a/daemon/nfs41_compound.c b/daemon/nfs41_compound.c index 236a437..dbde059 100644 --- a/daemon/nfs41_compound.c +++ b/daemon/nfs41_compound.c @@ -30,6 +30,8 @@ #include "nfs41_callback.h" #include "name_cache.h" #include "daemon_debug.h" +#include "rpc/rpc.h" +#include "rpc/auth_sspi.h" #define BUF_SIZE 1024 @@ -295,15 +297,63 @@ static bool_t recover_stateid(nfs_argop4 *argop, nfs41_session *session) return retry; } +static int create_new_rpc_auth(nfs41_session *session, uint32_t op, + nfs41_secinfo_info *secinfo) +{ + AUTH *auth = NULL; + int status = ERROR_NETWORK_UNREACHABLE, i; + uint32_t sec_flavor; + + for (i = 0; i < MAX_SECINFOS; i++) { + if (!secinfo[i].sec_flavor && !secinfo[i].type) + goto out; + if (secinfo[i].sec_flavor == RPCSEC_GSS) { + auth = authsspi_create_default(session->client->rpc->rpc, + session->client->rpc->server_name, secinfo[i].type); + if (auth == NULL) { + eprintf("handle_wrongsecinfo_noname: authsspi_create_default for " + "gsstype %s failed\n", gssauth_string(secinfo[i].type)); + continue; + } + sec_flavor = secinfo[i].type; + } else { + char machname[MAXHOSTNAMELEN + 1]; + gid_t gids[1]; + if (gethostname(machname, sizeof(machname)) == -1) { + eprintf("nfs41_rpc_clnt_create: gethostname failed\n"); + continue; + } + machname[sizeof(machname) - 1] = '\0'; + auth = authsys_create(machname, session->client->rpc->uid, + session->client->rpc->gid, 0, gids); + if (auth == NULL) { + eprintf("handle_wrongsecinfo_noname: authsys_create failed\n"); + continue; + } + sec_flavor = AUTH_SYS; + } + AcquireSRWLockExclusive(&session->client->rpc->lock); + session->client->rpc->sec_flavor = sec_flavor; + session->client->rpc->rpc->cl_auth = auth; + ReleaseSRWLockExclusive(&session->client->rpc->lock); + status = 0; + break; + } +out: + return status; +} + int compound_encode_send_decode( nfs41_session *session, nfs41_compound *compound, bool_t try_recovery) { - int status, retry_count = 0, delayby = 0; + int status, retry_count = 0, delayby = 0, secinfo_status; nfs41_sequence_args *args = (nfs41_sequence_args *)compound->args.argarray[0].arg; bool_t client_state_lost = FALSE; + uint32_t saved_sec_flavor; + AUTH *saved_auth; retry: /* send compound */ @@ -370,6 +420,7 @@ retry: if (status) { eprintf("nfs41_exchange_id() failed with %d\n", status); status = ERROR_BAD_NET_RESP; + goto out; } if (compound->args.argarray[0].op == OP_CREATE_SESSION) { @@ -492,6 +543,124 @@ restart_recovery: } } break; + case NFS4ERR_WRONGSEC: + { + nfs41_secinfo_info secinfo[MAX_SECINFOS]; + uint32_t op = compound->args.argarray[compound->res.resarray_count-1].op; + switch(op) { + case OP_PUTFH: + case OP_RESTOREFH: + case OP_LINK: + case OP_RENAME: + case OP_PUTROOTFH: + case OP_LOOKUP: + case OP_OPEN: + case OP_SECINFO_NO_NAME: + case OP_SECINFO: + if (compound->args.argarray[0].op == OP_SEQUENCE) { + nfs41_sequence_args *seq = (nfs41_sequence_args*) + compound->args.argarray[0].arg; + nfs41_session_free_slot(session, seq->sa_slotid); + } + /* from: 2.6.3.1.1.5. Put Filehandle Operation + SECINFO/SECINFO_NO_NAME + * The NFSv4.1 server MUST NOT return NFS4ERR_WRONGSEC to a put + * filehandle operation that is immediately followed by SECINFO or + * SECINFO_NO_NAME. The NFSv4.1 server MUST NOT return NFS4ERR_WRONGSEC + * from SECINFO or SECINFO_NO_NAME. + */ + if (compound->args.argarray[0].op == OP_SEQUENCE && + (compound->args.argarray[1].op == OP_PUTFH || + compound->args.argarray[1].op == OP_PUTROOTFH) && + (compound->args.argarray[2].op == OP_SECINFO_NO_NAME || + compound->args.argarray[2].op == OP_SECINFO)) { + dprintf(1, "SECINFO: BROKEN SERVER\n"); + goto out; + } + if (!try_recovery) + goto out; + if (!recovery_start_or_wait(session->client)) + goto do_retry; + ZeroMemory(secinfo, sizeof(nfs41_secinfo_info)*MAX_SECINFOS); + saved_sec_flavor = session->client->rpc->sec_flavor; + saved_auth = session->client->rpc->rpc->cl_auth; + if (op == OP_LOOKUP || op == OP_OPEN) { + const nfs41_component *name; + nfs41_path_fh *file = NULL, tmp; + if (compound->args.argarray[compound->res.resarray_count-2].op == OP_PUTFH) { + nfs41_putfh_args *putfh = (nfs41_putfh_args*) + compound->args.argarray[compound->res.resarray_count-2].arg; + file = putfh->file; + } else if (compound->args.argarray[compound->res.resarray_count-2].op == OP_GETATTR && + compound->args.argarray[compound->res.resarray_count-3].op == OP_GETFH) { + nfs41_getfh_res *getfh = (nfs41_getfh_res *) + compound->res.resarray[compound->res.resarray_count-3].res; + ZeroMemory(&tmp, sizeof(nfs41_path_fh)); + memcpy(&tmp.fh, getfh->fh, sizeof(nfs41_fh)); + file = &tmp; + } + else { + recovery_finish(session->client); + goto out; + } + + if (op == OP_LOOKUP) { + nfs41_lookup_args *largs = (nfs41_lookup_args *) + compound->args.argarray[compound->res.resarray_count-1].arg; + name = largs->name; + } else if (op == OP_OPEN) { + nfs41_op_open_args *oargs = (nfs41_op_open_args *) + compound->args.argarray[compound->res.resarray_count-1].arg; + name = oargs->claim.u.null.filename; + } + secinfo_status = nfs41_secinfo(session, file, name, secinfo); + if (secinfo_status) { + eprintf("nfs41_secinfo failed with %d\n", secinfo_status); + recovery_finish(session->client); + if (secinfo_status == NFS4ERR_BADSESSION) { + if (compound->args.argarray[0].op == OP_SEQUENCE) { + nfs41_sequence_args *seq = + (nfs41_sequence_args *)compound->args.argarray[0].arg; + nfs41_session_free_slot(session, seq->sa_slotid); + } + goto do_retry; + } + goto out_free_slot; + } + } + else { + nfs41_path_fh *file = NULL; + if (op == OP_PUTFH) { + nfs41_putfh_args *putfh = (nfs41_putfh_args*) + compound->args.argarray[compound->res.resarray_count-1].arg; + file = putfh->file; + } + secinfo_status = nfs41_secinfo_noname(session, file, secinfo); + if (secinfo_status) { + eprintf("nfs41_secinfo_noname failed with %d\n", secinfo_status); + recovery_finish(session->client); + if (compound->args.argarray[0].op == OP_SEQUENCE) { + nfs41_sequence_args *seq = + (nfs41_sequence_args *)compound->args.argarray[0].arg; + nfs41_session_free_slot(session, seq->sa_slotid); + } + goto out_free_slot; + } + } + secinfo_status = create_new_rpc_auth(session, op, secinfo); + if (!secinfo_status) { + auth_destroy(saved_auth); + recovery_finish(session->client); + // Need to retry only + goto do_retry; + } else { + AcquireSRWLockExclusive(&session->client->rpc->lock); + session->client->rpc->rpc->cl_auth = saved_auth; + ReleaseSRWLockExclusive(&session->client->rpc->lock); + recovery_finish(session->client); + } + break; + } + } } out_free_slot: if (compound->args.argarray[0].op == OP_SEQUENCE) { diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c index c241d22..2f4ba12 100644 --- a/daemon/nfs41_ops.c +++ b/daemon/nfs41_ops.c @@ -1682,15 +1682,19 @@ int nfs41_secinfo( if (status) goto out; - compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res); - putfh_args.file = file; - putfh_args.in_recovery = 0; + if (file == NULL) + compound_add_op(&compound, OP_PUTROOTFH, NULL, &putfh_res); + else { + compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res); + putfh_args.file = file; + putfh_args.in_recovery = 0; + } compound_add_op(&compound, OP_SECINFO, &secinfo_args, &secinfo_res); secinfo_args.name = name; secinfo_res.secinfo = secinfo; - status = compound_encode_send_decode(session, &compound, TRUE); + status = compound_encode_send_decode(session, &compound, FALSE); if (status) goto out; @@ -1701,6 +1705,7 @@ out: int nfs41_secinfo_noname( IN nfs41_session *session, + IN nfs41_path_fh *file, OUT nfs41_secinfo_info *secinfo) { int status; @@ -1709,6 +1714,7 @@ int nfs41_secinfo_noname( nfs_resop4 resops[3]; nfs41_sequence_args sequence_args; nfs41_sequence_res sequence_res; + nfs41_putfh_args putfh_args; nfs41_putfh_res putfh_res; nfs41_secinfo_noname_args noname_args; nfs41_secinfo_noname_res noname_res; @@ -1720,13 +1726,19 @@ int nfs41_secinfo_noname( if (status) goto out; - compound_add_op(&compound, OP_PUTROOTFH, NULL, &putfh_res); + if (file == NULL) + compound_add_op(&compound, OP_PUTROOTFH, NULL, &putfh_res); + else { + compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res); + putfh_args.file = file; + putfh_args.in_recovery = 0; + } compound_add_op(&compound, OP_SECINFO_NO_NAME, &noname_args, &noname_res); noname_args.type = SECINFO_STYLE4_CURRENT_FH; noname_res.secinfo = secinfo; - status = compound_encode_send_decode(session, &compound, TRUE); + status = compound_encode_send_decode(session, &compound, FALSE); if (status) goto out; diff --git a/daemon/nfs41_ops.h b/daemon/nfs41_ops.h index e9ed0a1..bdbd66c 100644 --- a/daemon/nfs41_ops.h +++ b/daemon/nfs41_ops.h @@ -1143,6 +1143,7 @@ int nfs41_secinfo( int nfs41_secinfo_noname( IN nfs41_session *session, + IN nfs41_path_fh *file, OUT nfs41_secinfo_info *secinfo); enum nfsstat4 pnfs_rpc_layoutget(