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
nfs4_secinfo_init(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
nfs4_secinfo_fini(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
secinfo2nfsflavor(sec_oid4 * mech_oid,rpc_gss_svc_t service)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 *
secinfo_create(servinfo4_t * svp,SECINFO4res * sec_info,char * servname)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
secinfo_free(sv_secinfo_t * secinfo)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
secinfo_check(servinfo4_t * svp)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
secinfo_update(servinfo4_t * svp,SECINFO4res * sec_info)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
save_mnt_secinfo(servinfo4_t * svp)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
check_mnt_secinfo(servinfo4_t * svp,vnode_t * vp)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
secinfo_tryroot_otw(mntinfo4_t * mi,cred_t * cr)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
comp_total(char * inpath)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
comp_getn(char * inpath,int nth,component4 * comp)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
nfs4secinfo_otw(mntinfo4_t * mi,cred_t * cr,servinfo4_t * svp,int isrecov)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
nfs4_secinfo_path(mntinfo4_t * mi,cred_t * cr,int isrecov)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
nfs4_secinfo_fh_otw(mntinfo4_t * mi,nfs4_sharedfh_t * fh,char * nm,cred_t * cr)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
nfs4_secinfo_vnode_otw(vnode_t * dvp,char * nm,cred_t * cr)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
nfs4_secinfo_vnode(vnode_t * vp,cred_t * cr,int isrecov)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
nfs4_secinfo_recov(mntinfo4_t * mi,vnode_t * vp1,vnode_t * vp2)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