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