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