1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * NFS Version 4 client side SECINFO code. 31 */ 32 33 #include <nfs/nfs4_clnt.h> 34 #include <nfs/nfs4.h> 35 #include <nfs/nfs_clnt.h> 36 #include <nfs/rnode4.h> 37 #include <sys/cmn_err.h> 38 #include <sys/cred.h> 39 #include <sys/systm.h> 40 41 /* 42 * Set up the security flavors supported in this release. 43 * In the order of potential usage. 44 */ 45 #define SECINFO_SUPPORT_COUNT 6 /* sys, krb5, krb5i, krb5p, none, dh */ 46 static char krb5_val[] = {'\x2A', '\x86', '\x48', '\x86', '\xF7', \ 47 '\x12', '\x01', '\x02', '\x02'}; 48 static sec_oid4 krb5_oid = {9, krb5_val}; 49 static SECINFO4res *secinfo_support; 50 51 /* XXX should come from auth.h, do the cleanup someday */ 52 extern void sec_clnt_freeinfo(struct sec_data *); 53 54 /* 55 * "nfsstat -m" needs to print out what flavor is used for a mount 56 * point. V3 kernel gets the nfs pseudo flavor from the userland and provides 57 * nfsstat with such information. However, in V4, we do not have nfs pseudo 58 * flavors mapping in the kernel for the rpcsec_gss data negotiated from 59 * the nfs server. 60 * 61 * XXX 62 * Hard coded the mapping in V4 for now. We should look into a possibility 63 * to return the rpcsec_gss mechanism and service information to nfsstat and 64 * perhaps have nfsstat print out the mech and service seperately... 65 * 66 * We should avoid referring to nfssec.conf file in V4. The original reason 67 * for having /etc/nfssec.conf file is because V3 MOUNT protocol can only 68 * return an integer for a flavor, thus the term "nfs pseudo flavor" is 69 * defined and the nfssec.conf file is used to map the nfs pseudo flavor 70 * to rpcsec_gss data (mech, service, default-qop). Now, V4 can return the 71 * rpcsec_gss data instead of an integer, so in theory, V4 should not need 72 * to depend on the nfssec.conf file anymore. 73 */ 74 #define NFS_FLAVOR_KRB5 390003 75 #define NFS_FLAVOR_KRB5I 390004 76 #define NFS_FLAVOR_KRB5P 390005 77 78 /* 79 * Currently, 6 flavors are supported: sys, krb5, krb5i, krb5p, dh, none. 80 * Without proper keys, krb5* or dh will fail. 81 * 82 * XXX kgss_indicate_mechs() should be able to tell us what gss mechanisms 83 * are supported on this host (/etc/gss/mech), thus nfs should be able to 84 * use them. However, the dh640 and dh1024 implementation are not nfs tested. 85 * Should look into using kgss_indicate_mechs when new gss mechanism is added. 86 */ 87 void 88 nfs4_secinfo_init(void) 89 { 90 secinfo4 *val; 91 int i; 92 93 secinfo_support = kmem_alloc(sizeof (SECINFO4res), KM_SLEEP); 94 secinfo_support->SECINFO4resok_len = SECINFO_SUPPORT_COUNT; 95 val = kmem_alloc( 96 secinfo_support->SECINFO4resok_len * sizeof (secinfo4), 97 KM_SLEEP); 98 99 val[0].flavor = AUTH_SYS; 100 val[0].flavor_info.oid.sec_oid4_len = 0; 101 val[0].flavor_info.oid.sec_oid4_val = NULL; 102 val[0].flavor_info.service = 0; 103 val[0].flavor_info.qop = 0; 104 105 /* add krb5, krb5i, krb5p */ 106 for (i = 1; i <= 3; i++) { 107 val[i].flavor = RPCSEC_GSS; 108 val[i].flavor_info.oid = krb5_oid; /* struct copy */ 109 val[i].flavor_info.service = i; 110 val[i].flavor_info.qop = 0; 111 } 112 113 val[4].flavor = AUTH_DH; 114 val[4].flavor_info.oid.sec_oid4_len = 0; 115 val[4].flavor_info.oid.sec_oid4_val = NULL; 116 val[4].flavor_info.service = 0; 117 val[4].flavor_info.qop = 0; 118 119 val[5].flavor = AUTH_NONE; 120 val[5].flavor_info.oid.sec_oid4_len = 0; 121 val[5].flavor_info.oid.sec_oid4_val = NULL; 122 val[5].flavor_info.service = 0; 123 val[5].flavor_info.qop = 0; 124 125 #if !defined(lint) 126 ASSERT(SECINFO_SUPPORT_COUNT == 6); 127 #endif 128 129 secinfo_support->SECINFO4resok_val = val; 130 } 131 132 /* 133 * clean up secinfo_support 134 */ 135 void 136 nfs4_secinfo_fini(void) 137 { 138 139 kmem_free(secinfo_support->SECINFO4resok_val, 140 secinfo_support->SECINFO4resok_len * sizeof (secinfo4)); 141 kmem_free(secinfo_support, sizeof (SECINFO4res)); 142 } 143 144 /* 145 * Map RPCSEC_GSS data to a nfs pseudo flavor number defined 146 * in the nfssec.conf file. 147 * 148 * mechanism service qop nfs-pseudo-flavor 149 * ---------------------------------------------------- 150 * kerberos_v5 none default 390003/krb5 151 * kerberos_v5 integrity default 390004/krb5i 152 * kerberos_v5 privacy default 390005/krb5p 153 * 154 * XXX need to re-visit the mapping semantics when a new 155 * security mechanism is to be added. 156 */ 157 int 158 secinfo2nfsflavor(sec_oid4 *mech_oid, rpc_gss_svc_t service) 159 { 160 /* Is this kerberos_v5? */ 161 if (bcmp(mech_oid->sec_oid4_val, krb5_oid.sec_oid4_val, 162 krb5_oid.sec_oid4_len) != 0) { 163 return (0); 164 } 165 166 /* for krb5, krb5i, krb5p mapping */ 167 switch (service) { 168 case RPC_GSS_SVC_NONE: 169 return (NFS_FLAVOR_KRB5); 170 case RPC_GSS_SVC_INTEGRITY: 171 return (NFS_FLAVOR_KRB5I); 172 case RPC_GSS_SVC_PRIVACY: 173 return (NFS_FLAVOR_KRB5P); 174 default: 175 break; 176 } 177 178 /* no mapping */ 179 return (0); 180 } 181 182 /* 183 * secinfo_create() maps the secinfo4 data coming over the wire 184 * to sv_secinfo data structure in servinfo4_t 185 */ 186 static sv_secinfo_t * 187 secinfo_create(servinfo4_t *svp, SECINFO4res *sec_info, char *servname) 188 { 189 uint_t i, seccnt, scnt; 190 sec_data_t *sdata; 191 sv_secinfo_t *sinfo; 192 uint_t len = sec_info->SECINFO4resok_len; 193 secinfo4 *value = sec_info->SECINFO4resok_val; 194 195 if (len == 0) 196 return (NULL); 197 198 seccnt = len; 199 200 /* 201 * If there is no valid sv_dhsec data available but an AUTH_DH 202 * is in the list, skip AUTH_DH flavor. 203 */ 204 if (!svp->sv_dhsec) { 205 for (i = 0; i < len; i++) { 206 if (value[i].flavor == AUTH_DH) 207 seccnt--; 208 } 209 } 210 211 if (seccnt == 0) 212 return (NULL); 213 214 sdata = kmem_alloc(sizeof (sec_data_t) * seccnt, KM_SLEEP); 215 scnt = 0; 216 for (i = 0; i < len; i++) { 217 secinfo4 *val = &value[i]; 218 gss_clntdata_t *data; 219 rpcsec_gss_info *info; 220 221 sdata[scnt].flags = 0; 222 sdata[scnt].rpcflavor = val->flavor; 223 224 switch (val->flavor) { 225 case RPCSEC_GSS: 226 data = kmem_alloc(sizeof (gss_clntdata_t), KM_SLEEP); 227 data->realm[0] = '\0'; 228 info = &val->flavor_info; 229 data->service = (rpc_gss_service_t)info->service; 230 data->qop = (uint_t)info->qop; 231 data->mechanism.length = info->oid.sec_oid4_len; 232 data->mechanism.elements = 233 kmem_alloc(info->oid.sec_oid4_len, KM_SLEEP); 234 bcopy(info->oid.sec_oid4_val, 235 data->mechanism.elements, info->oid.sec_oid4_len); 236 data->uname[0] = 'n'; data->uname[1] = 'f'; 237 data->uname[2] = 's'; data->uname[3] = '\0'; 238 (void) strcpy(data->inst, servname); 239 240 sdata[scnt].data = (caddr_t)data; 241 sdata[scnt].secmod = 242 secinfo2nfsflavor(&info->oid, info->service); 243 scnt++; 244 break; 245 case AUTH_DH: 246 if (svp->sv_dhsec) { 247 sdata[scnt] = *svp->sv_dhsec; 248 scnt++; 249 break; 250 } 251 /* no auth_dh data on the client, skip auth_dh */ 252 continue; 253 default: 254 sdata[scnt].secmod = val->flavor; 255 sdata[scnt].data = NULL; 256 scnt++; 257 break; 258 } 259 } 260 261 ASSERT(seccnt == scnt); 262 sinfo = kmem_alloc(sizeof (sv_secinfo_t), KM_SLEEP); 263 sinfo->count = seccnt; 264 sinfo->sdata = sdata; 265 266 return (sinfo); 267 } 268 269 /* 270 * secinfo_free() frees the malloc'd portion of a sv_secinfo_t in servinfo4_t. 271 * 272 * This is similar to sec_clnt_freeinfo() offered from rpcsec module, 273 * except that sec_clnt_freeinfo() frees up an individual secdata. 274 */ 275 void 276 secinfo_free(sv_secinfo_t *secinfo) 277 { 278 int i; 279 280 if (secinfo == NULL) 281 return; 282 283 for (i = 0; i < secinfo->count; i++) { 284 if (secinfo->sdata[i].rpcflavor == RPCSEC_GSS) { 285 gss_clntdata_t *data = (gss_clntdata_t *) 286 secinfo->sdata[i].data; 287 288 /* 289 * An auth handle may already cached in rpcsec_gss module 290 * per this secdata. Purge the cache entry before freeing 291 * up this secdata. Can't use sec_clnt_freeinfo since 292 * the allocation of secinfo is different from sec_data. 293 */ 294 (void) rpc_gss_secpurge((void *)&secinfo->sdata[i]); 295 296 kmem_free(data->mechanism.elements, data->mechanism.length); 297 kmem_free(data, sizeof (gss_clntdata_t)); 298 } 299 300 if (secinfo->sdata[i].rpcflavor == AUTH_DH) { 301 302 secinfo->sdata[i].data = NULL; /* release ref to sv_dhsec */ 303 304 /* 305 * No need to purge the auth_dh cache entry (e.g. call 306 * purge_authtab()) since the AUTH_DH data used here 307 * are always the same. 308 */ 309 } 310 } 311 kmem_free(secinfo->sdata, sizeof (sec_data_t) * secinfo->count); 312 kmem_free(secinfo, sizeof (sv_secinfo_t)); 313 } 314 315 /* 316 * Check if there is more secinfo to try. 317 * If TRUE, try again. 318 */ 319 static bool_t 320 secinfo_check(servinfo4_t *svp) 321 { 322 323 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); 324 if (svp->sv_secinfo == NULL) { 325 nfs_rw_exit(&svp->sv_lock); 326 return (FALSE); 327 } 328 329 svp->sv_secinfo->index++; 330 if (svp->sv_secinfo->index < svp->sv_secinfo->count) { 331 svp->sv_flags |= SV4_TRYSECINFO; 332 svp->sv_currsec = 333 &svp->sv_secinfo->sdata[svp->sv_secinfo->index]; 334 nfs_rw_exit(&svp->sv_lock); 335 return (TRUE); 336 } else { 337 svp->sv_secinfo->index = 0; 338 svp->sv_flags &= ~SV4_TRYSECINFO; 339 svp->sv_currsec = NULL; 340 nfs_rw_exit(&svp->sv_lock); 341 return (FALSE); 342 } 343 } 344 345 /* 346 * Update the secinfo related fields in svp. 347 * 348 * secinfo_update will free the previous sv_secinfo and update with 349 * the new secinfo. However, if the sv_secinfo is saved into sv_save_secinfo 350 * before the recovery starts via save_mnt_secinfo(), sv_secinfo will not 351 * be freed until the recovery is done. 352 */ 353 static void 354 secinfo_update(servinfo4_t *svp, SECINFO4res *sec_info) 355 { 356 357 sv_secinfo_t *newsecinfo; 358 359 /* 360 * Create secinfo before freeing the old one to make sure 361 * they are not using the same address. 362 */ 363 newsecinfo = secinfo_create(svp, sec_info, svp->sv_hostname); 364 365 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); 366 if (svp->sv_secinfo && svp->sv_secinfo != svp->sv_save_secinfo) { 367 secinfo_free(svp->sv_secinfo); 368 } 369 370 svp->sv_secinfo = newsecinfo; 371 if (svp->sv_secinfo) { 372 svp->sv_secinfo->index = 0; 373 svp->sv_flags |= SV4_TRYSECINFO; 374 svp->sv_currsec = 375 &svp->sv_secinfo->sdata[svp->sv_secinfo->index]; 376 } else { 377 svp->sv_flags &= ~SV4_TRYSECINFO; 378 svp->sv_currsec = NULL; 379 } 380 nfs_rw_exit(&svp->sv_lock); 381 } 382 383 /* 384 * Save the original mount point security information. 385 * 386 * sv_savesec saves the pointer of sv_currsec which points to one of the 387 * secinfo data in the sv_secinfo list. i.e. sv_currsec == &sv_secinfo[index]. 388 * 389 * sv_save_secinfo saves the pointer of sv_secinfo which is the list of 390 * secinfo data returned by the server. 391 */ 392 void 393 save_mnt_secinfo(servinfo4_t *svp) 394 { 395 396 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); 397 if (svp->sv_currsec) { 398 svp->sv_savesec = svp->sv_currsec; 399 svp->sv_save_secinfo = svp->sv_secinfo; 400 } else { 401 ASSERT(svp->sv_save_secinfo == NULL); 402 svp->sv_savesec = svp->sv_secdata; 403 } 404 nfs_rw_exit(&svp->sv_lock); 405 } 406 407 /* 408 * Check if we need to restore what is saved in sv_savesec and sv_save_secinfo 409 * to be the current secinfo information - sv_currsec and sv_secinfo. 410 * 411 * If op a node that is a stub for a crossed mount point, 412 * keep the original secinfo flavor for the current file system, 413 * not the crossed one. 414 */ 415 void 416 check_mnt_secinfo(servinfo4_t *svp, vnode_t *vp) 417 { 418 bool_t is_restore; 419 420 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); 421 422 is_restore = (vp == NULL || (VTOR4(vp)->r_flags & R4SRVSTUB)) && 423 svp->sv_save_secinfo && 424 (svp->sv_secinfo != svp->sv_save_secinfo); 425 426 if (is_restore) { 427 secinfo_free(svp->sv_secinfo); 428 if (svp->sv_savesec == svp->sv_secdata) { 429 ASSERT(svp->sv_save_secinfo == NULL); 430 svp->sv_secinfo = NULL; 431 svp->sv_currsec = NULL; 432 } else { 433 ASSERT(svp->sv_save_secinfo != NULL); 434 svp->sv_secinfo = svp->sv_save_secinfo; 435 svp->sv_currsec = svp->sv_savesec; 436 } 437 } else { 438 if (svp->sv_save_secinfo && 439 svp->sv_save_secinfo != svp->sv_secinfo) 440 secinfo_free(svp->sv_save_secinfo); 441 } 442 443 svp->sv_save_secinfo = NULL; 444 svp->sv_savesec = NULL; 445 446 nfs_rw_exit(&svp->sv_lock); 447 } 448 449 /* 450 * Use the security flavors supported on the client to try 451 * PUTROOTFH until a flavor is found. 452 * 453 * PUTROOTFH could return NFS4ERR_RESOURCE and NFS4ERR_WRONGSEC that 454 * may need a recovery action. This routine only handles NFS4ERR_WRONGSEC. 455 * For other recovery action, it returns ok to the caller for retry. 456 */ 457 static int 458 secinfo_tryroot_otw(mntinfo4_t *mi, cred_t *cr) 459 { 460 COMPOUND4args_clnt args; 461 COMPOUND4res_clnt res; 462 nfs_argop4 argop; 463 int doqueue = 1; 464 bool_t needrecov = FALSE; 465 nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS }; 466 467 /* use the flavors supported on the client */ 468 secinfo_update(mi->mi_curr_serv, secinfo_support); 469 470 /* Compound {Putroofh} */ 471 args.ctag = TAG_PUTROOTFH; 472 473 args.array_len = 1; 474 args.array = &argop; 475 476 argop.argop = OP_PUTROOTFH; 477 retry: 478 NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE, 479 "secinfo_tryroot_otw: %s call, mi 0x%p", 480 needrecov ? "recov" : "first", (void*)mi)); 481 482 rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e); 483 484 needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp); 485 if (e.error && !needrecov) { 486 return (e.error); 487 } 488 489 if (res.status == NFS4ERR_WRONGSEC) { 490 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 491 if (secinfo_check(mi->mi_curr_serv)) 492 goto retry; 493 /* 494 * Have tried all flavors supported on the client, 495 * but still get NFS4ERR_WRONGSEC. Nothing more can 496 * be done. 497 */ 498 return (geterrno4(res.status)); 499 } 500 501 if (needrecov) { 502 NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE, 503 "secinfo_tryroot_otw: let the caller retry\n")); 504 505 if (!e.error) 506 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 507 return (0); 508 } 509 510 if (res.status) { 511 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 512 return (geterrno4(res.status)); 513 } 514 515 /* 516 * Done. 517 * 518 * Now, mi->sv_curr_server->sv_currsec points to the flavor found. 519 * SV4_TRYSECINFO has been cleared in rfs4call. 520 * sv_currsec will be used. 521 */ 522 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 523 return (e.error); 524 } 525 526 /* 527 * Caculate the total number of components within a given pathname. 528 * Assuming the given pathname is not null. 529 * e.g. returns 5 for "/a/b/c/d/e" or "a/b/c/d/e" 530 * returns 0 for "/" 531 */ 532 static int 533 comp_total(char *inpath) 534 { 535 int tnum = 0; 536 char *slash; 537 538 while (*inpath != '\0') { 539 540 if (*inpath == '/') { 541 inpath++; 542 continue; 543 } 544 if ((slash = (char *)strchr(inpath, '/')) == NULL) { 545 tnum++; 546 break; 547 } else { 548 tnum++; 549 inpath = slash + 1; 550 } 551 } 552 553 return (tnum); 554 } 555 556 /* 557 * Get the pointer of the n-th component in the given path. 558 * Mark the preceeding '/' of the component to be '\0' when done. 559 * Assuming nth is > 0. 560 */ 561 static void 562 comp_getn(char *inpath, int nth, component4 *comp) 563 { 564 char *path = inpath, *comp_start, *slash = NULL; 565 int count = 0; 566 567 while ((count != nth) && (*path != '\0')) { 568 569 comp_start = path; 570 571 /* ignore slashes prior to the component name */ 572 while (*path == '/') 573 path++; 574 575 if (*path != '\0') { 576 comp_start = path; 577 count++; 578 } 579 580 if ((slash = strchr(path, '/')) == NULL) 581 break; 582 else 583 path = slash + 1; 584 } 585 586 if (count == nth) { 587 if (slash) 588 *slash = '\0'; 589 comp->utf8string_len = strlen(comp_start); 590 comp->utf8string_val = comp_start; 591 592 if (comp_start != inpath) { 593 comp_start--; 594 *comp_start = '\0'; 595 } 596 } else { 597 comp->utf8string_len = 0; 598 comp->utf8string_val = NULL; 599 } 600 } 601 602 /* 603 * SECINFO over the wire compound operation 604 * 605 * compound {PUTROOTFH, {LOOKUP parent-path}, SECINFO component} 606 * 607 * This routine assumes there is a component to work on, thus the 608 * given pathname (svp->sv_path) has to have at least 1 component. 609 * 610 * isrecov - TRUE if this routine is called from a recovery thread. 611 * 612 * nfs4secinfo_otw() only deals with NFS4ERR_WRONGSEC recovery. If this 613 * is already in a recovery thread, then setup the non-wrongsec recovery 614 * action thru nfs4_start_recovery and return to the outer loop in 615 * nfs4_recov_thread() for recovery. If this is not called from a recovery 616 * thread, then error out and let the caller decide what to do. 617 */ 618 static int 619 nfs4secinfo_otw(mntinfo4_t *mi, cred_t *cr, servinfo4_t *svp, int isrecov) 620 { 621 COMPOUND4args_clnt args; 622 COMPOUND4res_clnt res; 623 nfs_argop4 *argop; 624 nfs_resop4 *resop; 625 lookup4_param_t lookuparg; 626 uint_t path_len; 627 int doqueue; 628 int numops, num_argops; 629 char *tmp_path; 630 component4 comp; 631 uint_t ncomp, tcomp; 632 bool_t needrecov = FALSE; 633 nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS }; 634 635 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); 636 ncomp = tcomp = comp_total(svp->sv_path); 637 path_len = strlen(svp->sv_path); 638 nfs_rw_exit(&svp->sv_lock); 639 ASSERT(ncomp > 0); 640 641 retry: 642 tmp_path = kmem_alloc(path_len + 1, KM_SLEEP); 643 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); 644 bcopy(svp->sv_path, tmp_path, path_len + 1); 645 nfs_rw_exit(&svp->sv_lock); 646 comp_getn(tmp_path, ncomp, &comp); 647 648 args.ctag = TAG_SECINFO; 649 650 lookuparg.l4_getattrs = LKP4_NO_ATTRIBUTES; 651 lookuparg.argsp = &args; 652 lookuparg.resp = &res; 653 lookuparg.header_len = 1; /* Putrootfh */ 654 lookuparg.trailer_len = 1; /* Secinfo */ 655 lookuparg.ga_bits = NULL; 656 lookuparg.mi = mi; 657 658 /* setup LOOKUPs for parent path */ 659 (void) nfs4lookup_setup(tmp_path, &lookuparg, 0); 660 661 argop = args.array; 662 663 /* put root fh */ 664 argop[0].argop = OP_PUTROOTFH; 665 666 /* setup SECINFO op */ 667 num_argops = args.array_len; 668 argop[num_argops - 1].argop = OP_SECINFO; 669 argop[num_argops - 1].nfs_argop4_u.opsecinfo.name.utf8string_len = 670 comp.utf8string_len; 671 argop[num_argops - 1].nfs_argop4_u.opsecinfo.name.utf8string_val = 672 comp.utf8string_val; 673 674 doqueue = 1; 675 676 NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE, 677 "nfs4secinfo_otw: %s call, mi 0x%p", 678 needrecov ? "recov" : "first", (void*)mi)); 679 680 rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e); 681 682 needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp); 683 if (e.error && !needrecov) { 684 nfs4args_lookup_free(argop, num_argops); 685 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 686 kmem_free(tmp_path, path_len + 1); 687 return (e.error); 688 } 689 690 /* 691 * Secinfo compound op may fail with NFS4ERR_WRONGSEC from 692 * PUTROOTFH or LOOKUP. Special handling here to recover it. 693 */ 694 if (res.status == NFS4ERR_WRONGSEC) { 695 696 if (res.array_len == 1) { 697 /* 698 * If a flavor can not be found via trying 699 * all supported flavors on the client, no 700 * more operations. 701 */ 702 ncomp = tcomp; 703 nfs4args_lookup_free(argop, num_argops); 704 kmem_free(argop, 705 lookuparg.arglen * sizeof (nfs_argop4)); 706 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 707 kmem_free(tmp_path, path_len + 1); 708 709 if (e.error = secinfo_tryroot_otw(mi, cr)) { 710 return (e.error); 711 } 712 goto retry; 713 } 714 ncomp = res.array_len - 1; 715 nfs4args_lookup_free(argop, num_argops); 716 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 717 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 718 kmem_free(tmp_path, path_len + 1); 719 goto retry; 720 } 721 722 /* 723 * This routine does not do recovery for non NFS4ERR_WRONGSEC error. 724 * However, if this is already in a recovery thread, then 725 * set up the recovery action thru nfs4_start_recovery and 726 * return ok back to the outer loop in nfs4_recov_thread for 727 * recovery. 728 */ 729 if (needrecov) { 730 bool_t abort; 731 732 /* If not in a recovery thread, bail out */ 733 if (!isrecov) { 734 735 if (!e.error) { 736 e.error = geterrno4(res.status); 737 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 738 } 739 nfs4args_lookup_free(argop, num_argops); 740 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 741 kmem_free(tmp_path, path_len + 1); 742 return (e.error); 743 } 744 745 NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE, 746 "nfs4secinfo_otw: recovery in a recovery thread\n")); 747 748 abort = nfs4_start_recovery(&e, mi, NULL, 749 NULL, NULL, NULL, OP_SECINFO, NULL); 750 if (!e.error) { 751 e.error = geterrno4(res.status); 752 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 753 } 754 nfs4args_lookup_free(argop, num_argops); 755 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 756 kmem_free(tmp_path, path_len + 1); 757 if (abort == FALSE) { 758 /* 759 * Return ok to let the outer loop in 760 * nfs4_recov_thread continue with the recovery action. 761 */ 762 return (0); 763 } 764 return (e.error); 765 } 766 767 if (res.status) { 768 nfs4args_lookup_free(argop, num_argops); 769 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 770 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 771 kmem_free(tmp_path, path_len + 1); 772 return (geterrno4(res.status)); 773 } 774 775 /* 776 * Success! Now get the SECINFO result. 777 */ 778 numops = res.array_len; 779 resop = &res.array[numops-1]; /* secinfo res */ 780 ASSERT(resop->resop == OP_SECINFO); 781 782 if (resop->nfs_resop4_u.opsecinfo.SECINFO4resok_len == 0) { 783 /* 784 * Server does not return any flavor for this export point. 785 * Return EACCES. 786 */ 787 nfs4args_lookup_free(argop, num_argops); 788 kmem_free(tmp_path, path_len + 1); 789 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 790 kmem_free(argop, num_argops * sizeof (nfs_argop4)); 791 return (EACCES); 792 } 793 794 secinfo_update(mi->mi_curr_serv, &resop->nfs_resop4_u.opsecinfo); 795 796 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); 797 if (svp->sv_secinfo == NULL) { 798 nfs_rw_exit(&svp->sv_lock); 799 /* 800 * This could be because the server requires AUTH_DH, but 801 * the client does not have netname/syncaddr data 802 * from sv_dhsec. 803 */ 804 nfs4args_lookup_free(argop, num_argops); 805 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 806 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 807 kmem_free(tmp_path, path_len + 1); 808 return (EACCES); 809 } 810 nfs_rw_exit(&svp->sv_lock); 811 812 /* 813 * If this is not the original request, try again using the 814 * new secinfo data in mi. 815 */ 816 if (ncomp != tcomp) { 817 818 ncomp = tcomp; 819 nfs4args_lookup_free(argop, num_argops); 820 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 821 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 822 kmem_free(tmp_path, path_len + 1); 823 goto retry; 824 } 825 826 /* Done! */ 827 nfs4args_lookup_free(argop, num_argops); 828 kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); 829 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 830 kmem_free(tmp_path, path_len + 1); 831 832 return (0); /* got the secinfo */ 833 } 834 835 /* 836 * Get the security information per mount point. 837 * Use the server pathname to get the secinfo. 838 */ 839 int 840 nfs4_secinfo_path(mntinfo4_t *mi, cred_t *cr, int isrecov) 841 { 842 843 int error = 0; 844 int pathlen; 845 servinfo4_t *svp = mi->mi_curr_serv; 846 847 /* 848 * Get the server pathname that is being mounted on. 849 */ 850 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); 851 ASSERT(svp->sv_path != NULL); 852 pathlen = strlen(svp->sv_path); 853 854 /* 855 * If mounting server rootdir, use available secinfo list 856 * on the client. No SECINFO call here since SECINFO op 857 * expects a component name. 858 */ 859 if (pathlen == 1 && svp->sv_path[0] == '/') { 860 if (svp->sv_secinfo == NULL) { 861 nfs_rw_exit(&svp->sv_lock); 862 secinfo_update(svp, secinfo_support); 863 return (0); 864 } 865 nfs_rw_exit(&svp->sv_lock); 866 867 if (secinfo_check(svp)) 868 return (0); /* try again */ 869 870 /* no flavors in sv_secinfo work */ 871 return (EACCES); 872 } 873 nfs_rw_exit(&svp->sv_lock); 874 875 /* 876 * Get the secinfo from the server. 877 */ 878 error = nfs4secinfo_otw(mi, cr, svp, isrecov); 879 880 if (error) { 881 882 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); 883 if (svp->sv_secinfo) { 884 if (svp->sv_save_secinfo == svp->sv_secinfo) { 885 svp->sv_save_secinfo = NULL; 886 svp->sv_savesec = NULL; 887 } 888 secinfo_free(svp->sv_secinfo); 889 svp->sv_secinfo = NULL; 890 svp->sv_currsec = NULL; 891 svp->sv_flags &= ~SV4_TRYSECINFO; 892 } 893 894 if (svp->sv_save_secinfo) { 895 secinfo_free(svp->sv_save_secinfo); 896 svp->sv_save_secinfo = NULL; 897 svp->sv_savesec = NULL; 898 } 899 nfs_rw_exit(&svp->sv_lock); 900 } 901 902 return (error); 903 } 904 905 /* 906 * (secinfo) compound based on a given filehandle and component name. 907 * 908 * i.e. (secinfo) PUTFH (fh), SECINFO nm 909 */ 910 int 911 nfs4_secinfo_fh_otw(mntinfo4_t *mi, nfs4_sharedfh_t *fh, char *nm, cred_t *cr) 912 { 913 COMPOUND4args_clnt args; 914 COMPOUND4res_clnt res; 915 nfs_argop4 argop[2]; 916 nfs_resop4 *resop; 917 int num_argops, doqueue; 918 nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS }; 919 servinfo4_t *svp; 920 921 ASSERT(strlen(nm) > 0); 922 923 num_argops = 2; /* Putfh, Secinfo nm */ 924 args.ctag = TAG_SECINFO; 925 args.array_len = num_argops; 926 args.array = argop; 927 928 /* putfh fh */ 929 argop[0].argop = OP_CPUTFH; 930 argop[0].nfs_argop4_u.opcputfh.sfh = fh; 931 932 /* setup SECINFO op */ 933 argop[1].argop = OP_CSECINFO; 934 argop[1].nfs_argop4_u.opcsecinfo.cname = nm; 935 936 doqueue = 1; 937 938 rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, &e); 939 940 if (e.error) 941 return (e.error); 942 943 if (res.status) { 944 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 945 return (geterrno4(res.status)); 946 } 947 948 /* 949 * Success! Now get the SECINFO result. 950 */ 951 resop = &res.array[1]; /* secinfo res */ 952 ASSERT(resop->resop == OP_SECINFO); 953 954 if (resop->nfs_resop4_u.opsecinfo.SECINFO4resok_len == 0) { 955 /* 956 * Server does not return any flavor for this export point. 957 * Return EACCES. 958 */ 959 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 960 return (EACCES); 961 } 962 963 secinfo_update(mi->mi_curr_serv, &resop->nfs_resop4_u.opsecinfo); 964 965 svp = mi->mi_curr_serv; 966 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); 967 if (mi->mi_curr_serv->sv_secinfo == NULL) { 968 nfs_rw_exit(&svp->sv_lock); 969 /* 970 * This could be because the server requires AUTH_DH, but 971 * the client does not have netname/syncaddr data 972 * from sv_dhsec. 973 */ 974 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 975 return (EACCES); 976 } 977 nfs_rw_exit(&svp->sv_lock); 978 979 /* Done! */ 980 (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); 981 982 return (0); /* got the secinfo */ 983 } 984 985 /* 986 * Making secinfo operation with a given vnode. 987 * 988 * This routine is not used by the recovery thread. 989 * Mainly used in response to NFS4ERR_WRONGSEC from lookup. 990 */ 991 int 992 nfs4_secinfo_vnode_otw(vnode_t *dvp, char *nm, cred_t *cr) 993 { 994 ASSERT(strlen(nm) > 0); 995 996 return (nfs4_secinfo_fh_otw(VTOMI4(dvp), VTOR4(dvp)->r_fh, nm, cr)); 997 } 998 999 /* 1000 * Making secinfo operation with a given vnode if this vnode 1001 * has a parent node. If the given vnode is a root node, use 1002 * the pathname from the mntinfor4_t to do the secinfo call. 1003 * 1004 * This routine is mainly used by the recovery thread. 1005 */ 1006 int 1007 nfs4_secinfo_vnode(vnode_t *vp, cred_t *cr, int isrecov) 1008 { 1009 svnode_t *svp = VTOSV(vp); 1010 char *nm; 1011 int error = 0; 1012 1013 /* 1014 * If there is a parent filehandle, use it to get the secinfo, 1015 * otherwise, use mntinfo4_t pathname to get the secinfo. 1016 */ 1017 if (svp->sv_dfh) { 1018 nm = fn_name(svp->sv_name); /* get the actual component name */ 1019 error = nfs4_secinfo_fh_otw(VTOMI4(vp), svp->sv_dfh, nm, cr); 1020 kmem_free(nm, MAXNAMELEN); 1021 } else { 1022 error = nfs4_secinfo_path(VTOMI4(vp), cr, isrecov); 1023 } 1024 1025 return (error); 1026 } 1027 1028 /* 1029 * We are here because the client gets NFS4ERR_WRONGSEC. 1030 * 1031 * Get the security information from the server and indicate 1032 * a set of new security information is here to try. 1033 * Start with the server path that's mounted. 1034 */ 1035 int 1036 nfs4_secinfo_recov(mntinfo4_t *mi, vnode_t *vp1, vnode_t *vp2) 1037 { 1038 int error = 0; 1039 cred_t *cr, *lcr = NULL; 1040 servinfo4_t *svp = mi->mi_curr_serv; 1041 1042 /* 1043 * If the client explicitly specifies a preferred flavor to use 1044 * and gets NFS4ERR_WRONGSEC back, there is no need to negotiate 1045 * the flavor. 1046 */ 1047 (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); 1048 if (! (svp->sv_flags & SV4_TRYSECDEFAULT)) { 1049 error = geterrno4(NFS4ERR_WRONGSEC); 1050 nfs_rw_exit(&svp->sv_lock); 1051 } else { 1052 cr = crgetcred(); 1053 1054 if (svp->sv_secdata->uid != 0) { 1055 lcr = crdup(cr); 1056 (void) crsetugid(lcr, svp->sv_secdata->uid, 1057 crgetgid(cr)); 1058 } 1059 nfs_rw_exit(&svp->sv_lock); 1060 1061 if (vp1 == NULL && vp2 == NULL) { 1062 error = nfs4_secinfo_path(mi, cr, TRUE); 1063 1064 if (lcr && error == EACCES) 1065 error = nfs4_secinfo_path(mi, lcr, TRUE); 1066 } else if (vp1) { 1067 error = nfs4_secinfo_vnode(vp1, cr, TRUE); 1068 1069 if (lcr && error == EACCES) 1070 error = nfs4_secinfo_vnode(vp1, lcr, TRUE); 1071 } /* else */ 1072 /* ??? */ 1073 1074 crfree(cr); 1075 if (lcr != NULL) 1076 crfree(lcr); 1077 } 1078 1079 mutex_enter(&mi->mi_lock); 1080 mi->mi_recovflags &= ~MI4R_NEED_SECINFO; 1081 mutex_exit(&mi->mi_lock); 1082 1083 return (error); 1084 } 1085