1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include "nfs41_ops.h" 23 #include "daemon_debug.h" 24 #include "nfs41_xdr.h" 25 #include "nfs41_callback.h" 26 #include "nfs41_driver.h" /* for AUTH_SYS, AUTHGSS_KRB5s defines */ 27 28 #include "rpc/rpc.h" 29 #define SECURITY_WIN32 30 #include <security.h> 31 #include "rpc/auth_sspi.h" 32 33 static enum clnt_stat send_null(CLIENT *client) 34 { 35 struct timeval timeout = {10, 100}; 36 37 return clnt_call(client, 0, 38 (xdrproc_t)xdr_void, NULL, 39 (xdrproc_t)xdr_void, NULL, timeout); 40 } 41 42 static int get_client_for_netaddr( 43 IN const netaddr4 *netaddr, 44 IN uint32_t wsize, 45 IN uint32_t rsize, 46 IN nfs41_rpc_clnt *rpc, 47 OUT OPTIONAL char *server_name, 48 OUT CLIENT **client_out) 49 { 50 int status = ERROR_NETWORK_UNREACHABLE; 51 struct netconfig *nconf; 52 struct netbuf *addr; 53 CLIENT *client; 54 55 nconf = getnetconfigent(netaddr->netid); 56 if (nconf == NULL) 57 goto out; 58 59 addr = uaddr2taddr(nconf, netaddr->uaddr); 60 if (addr == NULL) 61 goto out_free_conf; 62 63 if (server_name) { 64 getnameinfo(addr->buf, addr->len, server_name, NI_MAXHOST, NULL, 0, 0); 65 dprintf(1, "servername is %s\n", server_name); 66 } 67 dprintf(1, "callback function %p args %p\n", nfs41_handle_callback, rpc); 68 client = clnt_tli_create(RPC_ANYFD, nconf, addr, NFS41_RPC_PROGRAM, 69 NFS41_RPC_VERSION, wsize, rsize, rpc ? proc_cb_compound_res : NULL, 70 rpc ? nfs41_handle_callback : NULL, rpc ? rpc : NULL); 71 if (client) { 72 *client_out = client; 73 status = NO_ERROR; 74 } 75 76 freenetbuf(addr); 77 out_free_conf: 78 freenetconfigent(nconf); 79 out: 80 return status; 81 } 82 83 static int get_client_for_multi_addr( 84 IN const multi_addr4 *addrs, 85 IN uint32_t wsize, 86 IN uint32_t rsize, 87 IN nfs41_rpc_clnt *rpc, 88 OUT OPTIONAL char *server_name, 89 OUT CLIENT **client_out, 90 OUT uint32_t *addr_index) 91 { 92 int status = ERROR_NETWORK_UNREACHABLE; 93 uint32_t i; 94 for (i = 0; i < addrs->count; i++) { 95 status = get_client_for_netaddr(&addrs->arr[i], 96 wsize, rsize, rpc, server_name, client_out); 97 if (status == NO_ERROR) { 98 *addr_index = i; 99 break; 100 } 101 } 102 return status; 103 } 104 105 int create_rpcsec_auth_client( 106 IN uint32_t sec_flavor, 107 IN char *server_name, 108 CLIENT *client 109 ) 110 { 111 int status = ERROR_NETWORK_UNREACHABLE; 112 113 switch (sec_flavor) { 114 case RPCSEC_AUTHGSS_KRB5: 115 client->cl_auth = authsspi_create_default(client, server_name, 116 RPCSEC_SSPI_SVC_NONE); 117 break; 118 case RPCSEC_AUTHGSS_KRB5I: 119 client->cl_auth = authsspi_create_default(client, server_name, 120 RPCSEC_SSPI_SVC_INTEGRITY); 121 break; 122 case RPCSEC_AUTHGSS_KRB5P: 123 client->cl_auth = authsspi_create_default(client, server_name, 124 RPCSEC_SSPI_SVC_PRIVACY); 125 break; 126 default: 127 eprintf("create_rpc_auth_client: unknown rpcsec flavor %d\n", 128 sec_flavor); 129 client->cl_auth = NULL; 130 } 131 132 if (client->cl_auth == NULL) { 133 eprintf("nfs41_rpc_clnt_create: failed to create %s\n", 134 secflavorop2name(sec_flavor)); 135 goto out; 136 } else 137 dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n", 138 secflavorop2name(sec_flavor)); 139 status = 0; 140 out: 141 return status; 142 } 143 144 /* Returns a client structure and an associated lock */ 145 int nfs41_rpc_clnt_create( 146 IN const multi_addr4 *addrs, 147 IN uint32_t wsize, 148 IN uint32_t rsize, 149 IN uint32_t uid, 150 IN uint32_t gid, 151 IN uint32_t sec_flavor, 152 OUT nfs41_rpc_clnt **rpc_out) 153 { 154 CLIENT *client; 155 nfs41_rpc_clnt *rpc; 156 uint32_t addr_index; 157 int status; 158 char machname[MAXHOSTNAMELEN + 1]; 159 gid_t gids[1]; 160 bool_t needcb = 1; 161 162 rpc = calloc(1, sizeof(nfs41_rpc_clnt)); 163 if (rpc == NULL) { 164 status = GetLastError(); 165 goto out; 166 } 167 #ifdef NO_CB_4_KRB5P 168 if (sec_flavor == RPCSEC_AUTHGSS_KRB5P) 169 needcb = 0; 170 #endif 171 rpc->needcb = needcb; 172 rpc->cond = CreateEvent(NULL, TRUE, FALSE, NULL); 173 if (rpc->cond == NULL) { 174 status = GetLastError(); 175 eprintf("CreateEvent failed %d\n", status); 176 goto out_free_rpc_clnt; 177 } 178 status = get_client_for_multi_addr(addrs, wsize, rsize, needcb?rpc:NULL, 179 rpc->server_name, &client, &addr_index); 180 if (status) { 181 clnt_pcreateerror("connecting failed"); 182 goto out_free_rpc_cond; 183 } 184 if (send_null(client) != RPC_SUCCESS) { 185 // XXX Do what here? 186 eprintf("nfs41_rpc_clnt_create: send_null failed\n"); 187 status = ERROR_NETWORK_UNREACHABLE; 188 goto out_err_client; 189 } 190 191 rpc->sec_flavor = sec_flavor; 192 if (sec_flavor == RPCSEC_AUTH_SYS) { 193 if (gethostname(machname, sizeof(machname)) == -1) { 194 eprintf("nfs41_rpc_clnt_create: gethostname failed\n"); 195 goto out_err_client; 196 } 197 machname[sizeof(machname) - 1] = '\0'; 198 client->cl_auth = authsys_create(machname, uid, gid, 0, gids); 199 if (client->cl_auth == NULL) { 200 eprintf("nfs41_rpc_clnt_create: failed to create rpc authsys\n"); 201 status = ERROR_NETWORK_UNREACHABLE; 202 goto out_err_client; 203 } 204 } else { 205 status = create_rpcsec_auth_client(sec_flavor, rpc->server_name, client); 206 if (status) { 207 eprintf("nfs41_rpc_clnt_create: failed to establish security " 208 "context with %s\n", rpc->server_name); 209 status = ERROR_NETWORK_UNREACHABLE; 210 goto out_err_client; 211 } else 212 dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n", 213 secflavorop2name(sec_flavor)); 214 } 215 rpc->rpc = client; 216 217 /* keep a copy of the address and buffer sizes for reconnect */ 218 memcpy(&rpc->addrs, addrs, sizeof(multi_addr4)); 219 /* save the index of the address we connected to */ 220 rpc->addr_index = addr_index; 221 rpc->wsize = wsize; 222 rpc->rsize = rsize; 223 rpc->is_valid_session = TRUE; 224 rpc->uid = uid; 225 rpc->gid = gid; 226 227 //initialize rpc client lock 228 InitializeSRWLock(&rpc->lock); 229 230 *rpc_out = rpc; 231 out: 232 return status; 233 out_err_client: 234 clnt_destroy(client); 235 out_free_rpc_cond: 236 CloseHandle(rpc->cond); 237 out_free_rpc_clnt: 238 free(rpc); 239 goto out; 240 } 241 242 /* Frees resources allocated in clnt_create */ 243 void nfs41_rpc_clnt_free( 244 IN nfs41_rpc_clnt *rpc) 245 { 246 auth_destroy(rpc->rpc->cl_auth); 247 clnt_destroy(rpc->rpc); 248 CloseHandle(rpc->cond); 249 free(rpc); 250 } 251 252 static bool_t rpc_renew_in_progress(nfs41_rpc_clnt *rpc, int *value) 253 { 254 bool_t status = FALSE; 255 AcquireSRWLockExclusive(&rpc->lock); 256 if (value) { 257 dprintf(1, "nfs41_rpc_renew_in_progress: setting value %d\n", *value); 258 rpc->in_recovery = *value; 259 if (!rpc->in_recovery) 260 SetEvent(rpc->cond); 261 } else { 262 status = rpc->in_recovery; 263 dprintf(1, "nfs41_rpc_renew_in_progress: returning value %d\n", status); 264 } 265 ReleaseSRWLockExclusive(&rpc->lock); 266 return status; 267 } 268 269 static bool_t rpc_should_retry(nfs41_rpc_clnt *rpc, uint32_t version) 270 { 271 bool_t status = 0; 272 AcquireSRWLockExclusive(&rpc->lock); 273 if (rpc->version > version) 274 status = 1; 275 ReleaseSRWLockExclusive(&rpc->lock); 276 return status; 277 } 278 279 static int rpc_reconnect( 280 IN nfs41_rpc_clnt *rpc) 281 { 282 CLIENT *client = NULL; 283 uint32_t addr_index; 284 int status; 285 286 AcquireSRWLockExclusive(&rpc->lock); 287 288 status = get_client_for_multi_addr(&rpc->addrs, rpc->wsize, rpc->rsize, 289 rpc->needcb?rpc:NULL, NULL, &client, &addr_index); 290 if (status) 291 goto out_unlock; 292 293 if(rpc->sec_flavor == RPCSEC_AUTH_SYS) 294 client->cl_auth = rpc->rpc->cl_auth; 295 else { 296 auth_destroy(rpc->rpc->cl_auth); 297 status = create_rpcsec_auth_client(rpc->sec_flavor, rpc->server_name, client); 298 if (status) { 299 eprintf("Failed to reestablish security context\n"); 300 status = ERROR_NETWORK_UNREACHABLE; 301 goto out_err_client; 302 } 303 } 304 if (send_null(client) != RPC_SUCCESS) { 305 eprintf("rpc_reconnect: send_null failed\n"); 306 status = ERROR_NETWORK_UNREACHABLE; 307 goto out_err_client; 308 } 309 310 clnt_destroy(rpc->rpc); 311 rpc->rpc = client; 312 rpc->addr_index = addr_index; 313 rpc->version++; 314 dprintf(1, "nfs41_send_compound: reestablished RPC connection\n"); 315 316 out_unlock: 317 ReleaseSRWLockExclusive(&rpc->lock); 318 319 /* after releasing the rpc lock, send a BIND_CONN_TO_SESSION if 320 * we need to associate the connection with the backchannel */ 321 if (status == NO_ERROR && rpc->needcb && 322 rpc->client && rpc->client->session) { 323 status = nfs41_bind_conn_to_session(rpc, 324 rpc->client->session->session_id, CDFC4_BACK_OR_BOTH); 325 if (status) 326 eprintf("nfs41_bind_conn_to_session() failed with %s\n", 327 nfs_error_string(status)); 328 status = NFS4_OK; 329 } 330 return status; 331 332 out_err_client: 333 clnt_destroy(client); 334 goto out_unlock; 335 } 336 337 int nfs41_send_compound( 338 IN nfs41_rpc_clnt *rpc, 339 IN char *inbuf, 340 OUT char *outbuf) 341 { 342 struct timeval timeout = {90, 100}; 343 enum clnt_stat rpc_status; 344 int status, count = 0, one = 1, zero = 0; 345 uint32_t version; 346 347 try_again: 348 AcquireSRWLockShared(&rpc->lock); 349 version = rpc->version; 350 rpc_status = clnt_call(rpc->rpc, 1, 351 (xdrproc_t)nfs_encode_compound, inbuf, 352 (xdrproc_t)nfs_decode_compound, outbuf, 353 timeout); 354 ReleaseSRWLockShared(&rpc->lock); 355 356 if (rpc_status != RPC_SUCCESS) { 357 eprintf("clnt_call returned rpc_status = %s\n", 358 rpc_error_string(rpc_status)); 359 switch(rpc_status) { 360 case RPC_CANTRECV: 361 case RPC_CANTSEND: 362 case RPC_TIMEDOUT: 363 case RPC_AUTHERROR: 364 if (++count > 3 || !rpc->is_valid_session) { 365 status = ERROR_NETWORK_UNREACHABLE; 366 break; 367 } 368 if (rpc_should_retry(rpc, version)) 369 goto try_again; 370 while (rpc_renew_in_progress(rpc, NULL)) { 371 status = WaitForSingleObject(rpc->cond, INFINITE); 372 if (status != WAIT_OBJECT_0) { 373 dprintf(1, "rpc_renew_in_progress: WaitForSingleObject failed\n"); 374 print_condwait_status(1, status); 375 status = ERROR_LOCK_VIOLATION; 376 goto out; 377 } 378 rpc_renew_in_progress(rpc, &zero); 379 goto try_again; 380 } 381 rpc_renew_in_progress(rpc, &one); 382 if (rpc_status == RPC_AUTHERROR && rpc->sec_flavor != RPCSEC_AUTH_SYS) { 383 AcquireSRWLockExclusive(&rpc->lock); 384 auth_destroy(rpc->rpc->cl_auth); 385 status = create_rpcsec_auth_client(rpc->sec_flavor, 386 rpc->server_name, rpc->rpc); 387 ReleaseSRWLockExclusive(&rpc->lock); 388 if (status) { 389 eprintf("Failed to reestablish security context\n"); 390 status = ERROR_NETWORK_UNREACHABLE; 391 goto out; 392 } 393 } else 394 if (rpc_reconnect(rpc)) 395 eprintf("rpc_reconnect: Failed to reconnect!\n"); 396 rpc_renew_in_progress(rpc, &zero); 397 goto try_again; 398 default: 399 eprintf("UNHANDLED RPC_ERROR: %d\n", rpc_status); 400 status = ERROR_NETWORK_UNREACHABLE; 401 goto out; 402 } 403 goto out; 404 } 405 406 status = 0; 407 out: 408 return status; 409 } 410