2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include "afs/param.h"
14 #if !defined(AFS_NONFSTRANS) || defined(AFS_AIX_IAUTH_ENV)
15 #include "afs/sysincludes.h" /* Standard vendor system headers */
16 #include "afsincludes.h" /* Afs-based standard headers */
17 #include "afs/afs_stats.h" /* statistics */
18 #include "afs/nfsclient.h"
19 #include "rx/rx_globals.h"
20 #include "afs/pagcb.h"
22 void afs_nfsclient_hold(), afs_PutNfsClientPag(), afs_nfsclient_GC();
23 static void afs_nfsclient_getcreds();
24 int afs_nfsclient_sysname(), afs_nfsclient_stats(), afs_nfsclient_checkhost();
25 afs_uint32
afs_nfsclient_gethost();
26 #ifdef AFS_AIX_IAUTH_ENV
27 int afs_allnfsreqs
, afs_nfscalls
;
30 /* routines exported to the "AFS exporter" layer */
31 struct exporterops nfs_exportops
= {
32 afs_nfsclient_reqhandler
,
34 afs_PutNfsClientPag
, /* Used to be afs_nfsclient_rele */
35 afs_nfsclient_sysname
,
38 afs_nfsclient_checkhost
,
43 struct nfsclientpag
*afs_nfspags
[NNFSCLIENTS
];
44 afs_lock_t afs_xnfspag
/*, afs_xnfsreq */ ;
45 extern struct afs_exporter
*afs_nfsexporter
;
47 /* Creates an nfsclientpag structure for the (uid, host) pair if one doesn't
48 * exist. RefCount is incremented and it's time stamped. */
49 static struct nfsclientpag
*
50 afs_GetNfsClientPag(afs_int32 uid
, afs_uint32 host
)
52 struct nfsclientpag
*np
;
55 #if defined(AFS_SGIMP_ENV)
56 osi_Assert(ISAFS_GLOCK());
58 AFS_STATCNT(afs_GetNfsClientPag
);
61 ObtainWriteLock(&afs_xnfspag
, 314);
62 for (np
= afs_nfspags
[i
]; np
; np
= np
->next
) {
63 if (np
->uid
== uid
&& np
->host
== host
) {
66 ReleaseWriteLock(&afs_xnfspag
);
70 /* next try looking for NOPAG dude, if we didn't find an exact match */
71 for (np
= afs_nfspags
[i
]; np
; np
= np
->next
) {
72 if (np
->uid
== NOPAG
&& np
->host
== host
) {
75 ReleaseWriteLock(&afs_xnfspag
);
79 np
= afs_osi_Alloc(sizeof(struct nfsclientpag
));
80 osi_Assert(np
!= NULL
);
81 memset(np
, 0, sizeof(struct nfsclientpag
));
82 /* Copy the necessary afs_exporter fields */
83 memcpy((char *)np
, (char *)afs_nfsexporter
, sizeof(struct afs_exporter
));
84 np
->next
= afs_nfspags
[i
];
90 ReleaseWriteLock(&afs_xnfspag
);
95 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
96 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
98 afs_PutNfsClientPag(np
)
99 struct nfsclientpag
*np
;
101 #if defined(AFS_SGIMP_ENV)
102 osi_Assert(ISAFS_GLOCK());
104 AFS_STATCNT(afs_PutNfsClientPag
);
109 /* Return the nfsclientpag structure associated with the (uid, host) or
110 * {pag, host} pair, if pag is nonzero. RefCount is incremented and it's
112 static struct nfsclientpag
*
113 afs_FindNfsClientPag(afs_int32 uid
, afs_uint32 host
, afs_int32 pag
)
115 struct nfsclientpag
*np
;
118 #if defined(AFS_SGIMP_ENV)
119 osi_Assert(ISAFS_GLOCK());
121 AFS_STATCNT(afs_FindNfsClientPag
);
123 ObtainWriteLock(&afs_xnfspag
, 315);
124 for (np
= afs_nfspags
[i
]; np
; np
= np
->next
) {
125 if (np
->host
== host
) {
126 if ((pag
&& pag
== np
->pag
) || (!pag
&& (uid
== np
->uid
))) {
128 np
->lastcall
= osi_Time();
129 ReleaseWriteLock(&afs_xnfspag
);
134 /* still not there, try looking for a wildcard dude */
135 for (np
= afs_nfspags
[i
]; np
; np
= np
->next
) {
136 if (np
->host
== host
) {
137 if (np
->uid
== NOPAG
) {
139 np
->lastcall
= osi_Time();
140 ReleaseWriteLock(&afs_xnfspag
);
145 ReleaseWriteLock(&afs_xnfspag
);
150 /* routine to initialize the exporter, made global so we can call it
153 struct afs_exporter
*afs_nfsexported
= 0;
154 static afs_int32 init_nfsexporter
= 0;
157 afs_nfsclient_init(void)
159 #if defined(AFS_SGIMP_ENV)
160 osi_Assert(ISAFS_GLOCK());
162 if (!init_nfsexporter
) {
163 extern struct afs_exporter
*exporter_add();
165 init_nfsexporter
= 1;
166 LOCK_INIT(&afs_xnfspag
, "afs_xnfspag");
168 exporter_add(0, &nfs_exportops
, EXP_EXPORTED
, EXP_NFS
, NULL
);
173 /* Main handler routine for the NFS exporter. It's called in the early
174 * phases of any remote call (via the NFS server or pioctl).
177 afs_nfsclient_reqhandler(struct afs_exporter
*exporter
,
179 afs_uint32 host
, afs_int32
*pagparam
,
180 struct afs_exporter
**outexporter
)
182 struct nfsclientpag
*np
, *tnp
;
183 extern struct unixuser
*afs_FindUser(), *afs_GetUser();
184 struct unixuser
*au
= 0;
185 afs_int32 uid
, pag
, code
= 0;
188 AFS_STATCNT(afs_nfsclient_reqhandler
);
189 if (!afs_nfsexporter
)
190 afs_nfsexporter
= afs_nfsexported
;
192 afs_nfsexporter
->exp_stats
.calls
++;
193 if (!(afs_nfsexporter
->exp_states
& EXP_EXPORTED
)) {
194 /* No afs requests accepted as long as EXPORTED flag is turned 'off'.
195 * Set/Reset via a pioctl call (fs exportafs). Note that this is on
196 * top of the /etc/exports nfs requirement (i.e. /afs must be
197 * exported to all or whomever there too!)
199 afs_nfsexporter
->exp_stats
.rejectedcalls
++;
202 /* ObtainWriteLock(&afs_xnfsreq); */
203 pag
= PagInCred(*cred
);
204 #if defined(AFS_SUN510_ENV)
205 uid
= crgetuid(*cred
);
207 uid
= afs_cr_uid(*cred
);
209 /* Do this early, so pag management knows */
210 afs_set_cr_rgid(*cred
, NFSXLATOR_CRED
); /* Identify it as nfs xlator call */
211 if ((afs_nfsexporter
->exp_states
& EXP_CLIPAGS
) && pag
!= NOPAG
) {
213 } else if (pag
!= NOPAG
) {
214 /* Do some minimal pag verification */
215 if (pag
> getpag()) {
216 pag
= NOPAG
; /* treat it as not paged since couldn't be good */
218 if ((au
= afs_FindUser(pag
, -1, READ_LOCK
))) {
221 afs_PutUser(au
, READ_LOCK
);
225 pag
= NOPAG
; /* No unixuser struct so pag not trusted */
228 np
= afs_FindNfsClientPag(uid
, host
, 0);
229 afs_Trace4(afs_iclSetp
, CM_TRACE_NFSREQH
, ICL_TYPE_INT32
, pag
,
230 ICL_TYPE_LONG
, afs_cr_uid(*cred
), ICL_TYPE_INT32
, host
,
231 ICL_TYPE_POINTER
, np
);
232 /* If remote-pags are enabled, we are no longer interested in what PAG
233 * they claimed, and from here on we should behave as if they claimed
234 * none at all, which is to say we use the (local) pag named in the
235 * nfsclientpag structure (if any). This is deferred until here so
236 * that we can log the PAG they claimed.
238 if ((afs_nfsexporter
->exp_states
& EXP_CLIPAGS
))
241 /* Even if there is a "good" pag coming in we don't accept it if no
242 * nfsclientpag struct exists for the user since that would mean
243 * that the translator rebooted and therefore we ignore all older
246 if ((code
= setpag(cred
, -1, &pag
, 0))) {
248 afs_PutUser(au
, READ_LOCK
);
249 /* ReleaseWriteLock(&afs_xnfsreq); */
250 #if defined(KERNEL_HAVE_UERROR)
255 np
= afs_GetNfsClientPag(uid
, host
);
257 np
->client_uid
= afs_cr_uid(*cred
);
260 if ((code
= setpag(cred
, np
->pag
, &pag
, 0))) {
261 afs_PutNfsClientPag(np
);
262 /* ReleaseWriteLock(&afs_xnfsreq); */
263 #if defined(KERNEL_HAVE_UERROR)
268 } else if (au
->exporter
269 && ((struct afs_exporter
*)np
!= au
->exporter
)) {
270 tnp
= (struct nfsclientpag
*)au
->exporter
;
271 if (tnp
->uid
&& (tnp
->uid
!= (afs_int32
) - 2)) { /* allow "root" initiators */
272 /* Pag doesn't belong to caller; treat it as an unpaged call too */
273 if ((code
= setpag(cred
, np
->pag
, &pag
, 0))) {
274 afs_PutNfsClientPag(np
);
275 afs_PutUser(au
, READ_LOCK
);
276 /* ReleaseWriteLock(&afs_xnfsreq); */
277 #if defined(KERNEL_HAVE_UERROR)
282 afs_nfsexporter
->exp_stats
.invalidpag
++;
287 afs_PutUser(au
, READ_LOCK
);
288 /* do not get a lock on au; afs_nfsclient_getcreds may write-lock the
290 au
= afs_GetUser(pag
, -1, 0);
291 if (!(au
->exporter
)) { /* Created new unixuser struct */
292 np
->refCount
++; /* so it won't disappear */
293 au
->exporter
= (struct afs_exporter
*)np
;
294 if ((afs_nfsexporter
->exp_states
& EXP_CALLBACK
))
295 afs_nfsclient_getcreds(au
);
296 } else while (au
->states
& UNFSGetCreds
) {
297 afs_osi_Sleep((void *)au
);
300 *outexporter
= (struct afs_exporter
*)np
;
302 /* ReleaseWriteLock(&afs_xnfsreq); */
307 afs_nfsclient_getcreds(struct unixuser
*au
)
309 struct nfsclientpag
*np
= (struct nfsclientpag
*)(au
->exporter
);
310 struct rx_securityClass
*csec
;
311 struct rx_connection
*tconn
;
312 union tokenUnion
*tokenPtr
;
313 struct rxkadToken
*token
;
314 SysNameList tsysnames
;
319 int code
, i
, cellnum
;
321 au
->states
|= UNFSGetCreds
;
322 memset(&tcreds
, 0, sizeof(tcreds
));
323 memset(&tsysnames
, 0, sizeof(tsysnames
));
325 /* Get a connection */
326 /* This sucks a little. We should cache the connections or something.
327 * But at this point I don't yet think it's worth the effort.
329 csec
= rxnull_NewClientSecurityObject();
331 tconn
= rx_NewConnection(np
->host
, htons(7001), PAGCB_SERVICEID
, csec
, 0);
334 /* Get the sysname, if needed */
335 if (!np
->sysnamecount
) {
337 code
= PAGCB_GetSysName(tconn
, np
->uid
, &tsysnames
);
340 tsysnames
.SysNameList_len
<= 0 ||
341 tsysnames
.SysNameList_len
> MAXNUMSYSNAMES
)
344 for(i
= 0; i
< np
->sysnamecount
; i
++)
345 afs_osi_Free(np
->sysname
[i
], MAXSYSNAME
);
347 np
->sysnamecount
= tsysnames
.SysNameList_len
;
348 for(i
= 0; i
< np
->sysnamecount
; i
++)
349 np
->sysname
[i
] = tsysnames
.SysNameList_val
[i
].sysname
;
350 afs_osi_Free(tsysnames
.SysNameList_val
,
351 tsysnames
.SysNameList_len
* sizeof(SysNameEnt
));
354 /* Get credentials */
356 code
= PAGCB_GetCreds(tconn
, np
->uid
, &tcreds
);
361 /* Now, set the credentials they gave us... */
362 for (i
= 0; i
< tcreds
.CredInfos_len
; i
++) {
363 tcred
= &tcreds
.CredInfos_val
[i
];
365 /* Find the cell. If it is unknown to us, punt this entry. */
366 tcell
= afs_GetCellByName(tcred
->cellname
, READ_LOCK
);
367 afs_osi_Free(tcred
->cellname
, strlen(tcred
->cellname
) + 1);
369 memset(tcred
->ct
.HandShakeKey
, 0, 8);
370 memset(tcred
->st
.st_val
, 0, tcred
->st
.st_len
);
371 afs_osi_Free(tcred
->st
.st_val
, tcred
->st
.st_len
);
374 cellnum
= tcell
->cellNum
;
375 afs_PutCell(tcell
, READ_LOCK
);
377 /* Find the appropriate unixuser. This might be the same as
378 * the one we were passed (au), but that's OK.
380 tu
= afs_GetUser(np
->pag
, cellnum
, WRITE_LOCK
);
381 if (!(tu
->exporter
)) { /* Created new unixuser struct */
382 np
->refCount
++; /* so it won't disappear */
383 tu
->exporter
= (struct afs_exporter
*)np
;
386 afs_FreeTokens(&tu
->tokens
);
388 /* Add a new rxkad token. Using the afs_AddRxkadToken interface
389 * would require another copy, so we do this the hard way */
390 tokenPtr
= afs_AddToken(&tu
->tokens
, 2);
391 token
= &tokenPtr
->rxkad
;
392 token
->ticket
= tcred
->st
.st_val
;
393 token
->ticketLen
= tcred
->st
.st_len
;
395 /* copy the clear token */
396 memset(&token
->clearToken
, 0, sizeof(token
->clearToken
));
397 memcpy(token
->clearToken
.HandShakeKey
, tcred
->ct
.HandShakeKey
, 8);
398 memset(tcred
->ct
.HandShakeKey
, 0, 8);
399 token
->clearToken
.AuthHandle
= tcred
->ct
.AuthHandle
;
400 token
->clearToken
.ViceId
= tcred
->ct
.ViceId
;
401 token
->clearToken
.BeginTimestamp
= tcred
->ct
.BeginTimestamp
;
402 token
->clearToken
.EndTimestamp
= tcred
->ct
.EndTimestamp
;
404 /* Set everything else, reset connections, and move on. */
405 tu
->viceId
= tcred
->vid
;
406 tu
->states
|= UHasTokens
;
407 tu
->states
&= ~UTokensBad
;
408 afs_SetPrimary(tu
, !!(tcred
->states
& UPrimary
));
409 tu
->tokenTime
= osi_Time();
410 afs_ResetUserConns(tu
);
411 afs_PutUser(tu
, WRITE_LOCK
);
413 afs_osi_Free(tcreds
.CredInfos_val
, tcreds
.CredInfos_len
* sizeof(CredInfo
));
417 rx_DestroyConnection(tconn
);
419 au
->states
&= ~UNFSGetCreds
;
420 afs_osi_Wakeup((void *)au
);
424 /* It's called whenever a new unixuser structure is created for the remote
425 * user associated with the nfsclientpag structure, np */
427 afs_nfsclient_hold(struct nfsclientpag
*np
)
429 #if defined(AFS_SGIMP_ENV)
430 osi_Assert(ISAFS_GLOCK());
432 AFS_STATCNT(afs_nfsclient_hold
);
437 /* check if this exporter corresponds to the specified host */
439 afs_nfsclient_checkhost(struct nfsclientpag
*np
, afs_uint32 host
)
441 if (np
->type
!= EXP_NFS
)
443 return np
->host
== host
;
447 /* get the host for this exporter, or 0 if there is an error */
449 afs_nfsclient_gethost(struct nfsclientpag
*np
)
451 if (np
->type
!= EXP_NFS
)
457 /* if inname is non-null, a new system name value is set for the remote
458 * user (inname contains the new sysname). In all cases, outname returns
459 * the current sysname value for this remote user */
461 afs_nfsclient_sysname(struct nfsclientpag
*np
, char *inname
,
462 char ***outname
, int *num
, int allpags
)
464 struct nfsclientpag
*tnp
;
468 #if defined(AFS_SGIMP_ENV)
469 osi_Assert(ISAFS_GLOCK());
471 AFS_STATCNT(afs_nfsclient_sysname
);
473 /* update every client, not just the one making the request */
475 ObtainWriteLock(&afs_xnfspag
, 315);
476 for (tnp
= afs_nfspags
[i
]; tnp
; tnp
= tnp
->next
) {
477 if (tnp
!= np
&& tnp
->host
== np
->host
)
478 afs_nfsclient_sysname(tnp
, inname
, outname
, num
, -1);
480 ReleaseWriteLock(&afs_xnfspag
);
483 for(count
=0; count
< np
->sysnamecount
;++count
) {
484 afs_osi_Free(np
->sysname
[count
], MAXSYSNAME
);
485 np
->sysname
[count
] = NULL
;
487 for(count
=0; count
< *num
;++count
) {
488 np
->sysname
[count
]= afs_osi_Alloc(MAXSYSNAME
);
489 osi_Assert(np
->sysname
[count
] != NULL
);
492 for(count
=0; count
< *num
;++count
) {
494 memcpy(np
->sysname
[count
], cp
, t
+1); /* include null */
497 np
->sysnamecount
= *num
;
500 /* Don't touch our arguments when called recursively */
501 *outname
= np
->sysname
;
502 *num
= np
->sysnamecount
;
504 return ENODEV
; /* XXX */
510 /* Garbage collect routine for the nfs exporter. When pag is -1 then all
511 * entries are removed (used by the nfsclient_shutdown routine); else if
512 * it's non zero then only the entry with that pag is removed, else all
513 * "timedout" entries are removed. TimedOut entries are those who have no
514 * "unixuser" structures associated with them (i.e. unixusercnt == 0) and
515 * they haven't had any activity the last NFSCLIENTGC seconds */
517 afs_nfsclient_GC(struct afs_exporter
*exporter
,
520 struct nfsclientpag
*np
, **tnp
, *nnp
;
521 afs_int32 i
, delflag
;
524 #if defined(AFS_SGIMP_ENV)
525 osi_Assert(ISAFS_GLOCK());
527 AFS_STATCNT(afs_nfsclient_GC
);
528 ObtainWriteLock(&afs_xnfspag
, 316);
529 for (i
= 0; i
< NNFSCLIENTS
; i
++) {
530 for (tnp
= &afs_nfspags
[i
], np
= *tnp
; np
; np
= nnp
) {
533 if (np
->refCount
== 0 && np
->lastcall
< osi_Time() - NFSCLIENTGC
)
535 if ((pag
== -1) || (!pag
&& delflag
)
536 || (pag
&& (np
->refCount
== 0) && (np
->pag
== pag
))) {
538 for(count
=0; count
< np
->sysnamecount
;++count
) {
539 afs_osi_Free(np
->sysname
[count
], MAXSYSNAME
);
541 afs_osi_Free(np
, sizeof(struct nfsclientpag
));
547 ReleaseWriteLock(&afs_xnfspag
);
552 afs_nfsclient_stats(struct afs_exporter
*export
)
554 /* Nothing much to do here yet since most important stats are collected
555 * directly in the afs_exporter structure itself */
556 AFS_STATCNT(afs_nfsclient_stats
);
561 /* This is exposed so that vop_fid can test it, even if iauth is not
564 extern int afs_iauth_initd
;
567 #ifdef AFS_AIX_IAUTH_ENV
568 char *afs_nfs_id
= "AFSNFSTRANS";
569 /* afs_iauth_verify is the AFS authenticator for NFS.
574 afs_iauth_verify(long id
, fsid_t
* fsidp
, long host
, int uid
,
575 afs_ucred_t
*credp
, struct exportinfo
*exp
)
578 struct nfsclientpag
*nfs_pag
;
580 struct afs_exporter
*outexporter
= 0;
583 /* Still needs basic test to see if exporter is on. And need to check the
584 * whole no submounts bit.
588 return 0; /* not us. */
590 /* Only care if it's AFS */
591 if ((fsidp
->val
[0] != AFS_VFSMAGIC
) || (fsidp
->val
[1] != AFS_VFSFSID
)) {
597 afs_nfsclient_reqhandler((struct afs_exporter
*)0, &credp
, host
,
598 &dummypag
, &outexporter
);
599 if (!code
&& outexporter
)
600 EXP_RELE(outexporter
);
603 /* ensure anonymous cred. */
604 afs_set_cr_uid(credp
, (uid_t
) -2; /* anonymous */
605 afs_set_cr_ruid(credp
, (uid_t
) -2;
608 /* Mark this thread as an NFS translator thread. */
609 afs_set_cr_rgid(credp
, NFSXLATOR_CRED
);
615 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
616 * and -1 on failure. Can fail because DFS has already registered.
619 afs_iauth_register(void)
621 if (nfs_iauth_register((unsigned long)afs_nfs_id
, afs_iauth_verify
))
629 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
632 afs_iauth_unregister(void)
635 nfs_iauth_unregister((unsigned long)afs_nfs_id
);
638 #endif /* AFS_AIX_IAUTH_ENV */
643 shutdown_nfsclnt(void)
645 #if defined(AFS_SGIMP_ENV)
646 osi_Assert(ISAFS_GLOCK());
648 AFS_STATCNT(afs_nfsclient_shutdown
);
649 #ifdef AFS_AIX_IAUTH_ENV
650 afs_iauth_register();
652 afs_nfsclient_GC(afs_nfsexporter
, -1);
653 init_nfsexporter
= 0;
655 #endif /* AFS_NONFSTRANS */