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 = (struct nfsclientpag *)afs_osi_Alloc(sizeof(struct nfsclientpag));
80 memset(np, 0, sizeof(struct nfsclientpag));
81 /* Copy the necessary afs_exporter fields */
82 memcpy((char *)np, (char *)afs_nfsexporter, sizeof(struct afs_exporter));
83 np->next = afs_nfspags[i];
89 ReleaseWriteLock(&afs_xnfspag);
94 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
95 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
97 afs_PutNfsClientPag(np)
98 struct nfsclientpag *np;
100 #if defined(AFS_SGIMP_ENV)
101 osi_Assert(ISAFS_GLOCK());
103 AFS_STATCNT(afs_PutNfsClientPag);
108 /* Return the nfsclientpag structure associated with the (uid, host) or
109 * {pag, host} pair, if pag is nonzero. RefCount is incremented and it's
111 static struct nfsclientpag *
112 afs_FindNfsClientPag(afs_int32 uid, afs_uint32 host, afs_int32 pag)
114 struct nfsclientpag *np;
117 #if defined(AFS_SGIMP_ENV)
118 osi_Assert(ISAFS_GLOCK());
120 AFS_STATCNT(afs_FindNfsClientPag);
122 ObtainWriteLock(&afs_xnfspag, 315);
123 for (np = afs_nfspags[i]; np; np = np->next) {
124 if (np->host == host) {
125 if ((pag && pag == np->pag) || (!pag && (uid == np->uid))) {
127 np->lastcall = osi_Time();
128 ReleaseWriteLock(&afs_xnfspag);
133 /* still not there, try looking for a wildcard dude */
134 for (np = afs_nfspags[i]; np; np = np->next) {
135 if (np->host == host) {
136 if (np->uid == NOPAG) {
138 np->lastcall = osi_Time();
139 ReleaseWriteLock(&afs_xnfspag);
144 ReleaseWriteLock(&afs_xnfspag);
149 /* routine to initialize the exporter, made global so we can call it
152 struct afs_exporter *afs_nfsexported = 0;
153 static afs_int32 init_nfsexporter = 0;
156 afs_nfsclient_init(void)
158 #if defined(AFS_SGIMP_ENV)
159 osi_Assert(ISAFS_GLOCK());
161 if (!init_nfsexporter) {
162 extern struct afs_exporter *exporter_add();
164 init_nfsexporter = 1;
165 LOCK_INIT(&afs_xnfspag, "afs_xnfspag");
167 exporter_add(0, &nfs_exportops, EXP_EXPORTED, EXP_NFS, NULL);
172 /* Main handler routine for the NFS exporter. It's called in the early
173 * phases of any remote call (via the NFS server or pioctl).
176 afs_nfsclient_reqhandler(struct afs_exporter *exporter,
178 afs_uint32 host, afs_int32 *pagparam,
179 struct afs_exporter **outexporter)
181 struct nfsclientpag *np, *tnp;
182 extern struct unixuser *afs_FindUser(), *afs_GetUser();
183 struct unixuser *au = 0;
184 afs_int32 uid, pag, code = 0;
187 AFS_STATCNT(afs_nfsclient_reqhandler);
188 if (!afs_nfsexporter)
189 afs_nfsexporter = afs_nfsexported;
191 afs_nfsexporter->exp_stats.calls++;
192 if (!(afs_nfsexporter->exp_states & EXP_EXPORTED)) {
193 /* No afs requests accepted as long as EXPORTED flag is turned 'off'.
194 * Set/Reset via a pioctl call (fs exportafs). Note that this is on
195 * top of the /etc/exports nfs requirement (i.e. /afs must be
196 * exported to all or whomever there too!)
198 afs_nfsexporter->exp_stats.rejectedcalls++;
201 /* ObtainWriteLock(&afs_xnfsreq); */
202 pag = PagInCred(*cred);
203 #if defined(AFS_SUN510_ENV)
204 uid = crgetuid(*cred);
206 uid = afs_cr_uid(*cred);
208 /* Do this early, so pag management knows */
209 afs_set_cr_rgid(*cred, NFSXLATOR_CRED); /* Identify it as nfs xlator call */
210 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
212 } else if (pag != NOPAG) {
213 /* Do some minimal pag verification */
214 if (pag > getpag()) {
215 pag = NOPAG; /* treat it as not paged since couldn't be good */
217 if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
220 afs_PutUser(au, READ_LOCK);
224 pag = NOPAG; /* No unixuser struct so pag not trusted */
227 np = afs_FindNfsClientPag(uid, host, 0);
228 afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
229 ICL_TYPE_LONG, afs_cr_uid(*cred), ICL_TYPE_INT32, host,
230 ICL_TYPE_POINTER, np);
231 /* If remote-pags are enabled, we are no longer interested in what PAG
232 * they claimed, and from here on we should behave as if they claimed
233 * none at all, which is to say we use the (local) pag named in the
234 * nfsclientpag structure (if any). This is deferred until here so
235 * that we can log the PAG they claimed.
237 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
240 /* Even if there is a "good" pag coming in we don't accept it if no
241 * nfsclientpag struct exists for the user since that would mean
242 * that the translator rebooted and therefore we ignore all older
245 if ((code = setpag(cred, -1, &pag, 0))) {
247 afs_PutUser(au, READ_LOCK);
248 /* ReleaseWriteLock(&afs_xnfsreq); */
249 #if defined(KERNEL_HAVE_UERROR)
254 np = afs_GetNfsClientPag(uid, host);
256 np->client_uid = afs_cr_uid(*cred);
259 if ((code = setpag(cred, np->pag, &pag, 0))) {
260 afs_PutNfsClientPag(np);
261 /* ReleaseWriteLock(&afs_xnfsreq); */
262 #if defined(KERNEL_HAVE_UERROR)
267 } else if (au->exporter
268 && ((struct afs_exporter *)np != au->exporter)) {
269 tnp = (struct nfsclientpag *)au->exporter;
270 if (tnp->uid && (tnp->uid != (afs_int32) - 2)) { /* allow "root" initiators */
271 /* Pag doesn't belong to caller; treat it as an unpaged call too */
272 if ((code = setpag(cred, np->pag, &pag, 0))) {
273 afs_PutNfsClientPag(np);
274 afs_PutUser(au, READ_LOCK);
275 /* ReleaseWriteLock(&afs_xnfsreq); */
276 #if defined(KERNEL_HAVE_UERROR)
281 afs_nfsexporter->exp_stats.invalidpag++;
286 afs_PutUser(au, READ_LOCK);
287 au = afs_GetUser(pag, -1, WRITE_LOCK);
288 if (!(au->exporter)) { /* Created new unixuser struct */
289 np->refCount++; /* so it won't disappear */
290 au->exporter = (struct afs_exporter *)np;
291 if ((afs_nfsexporter->exp_states & EXP_CALLBACK))
292 afs_nfsclient_getcreds(au);
293 } else while (au->states & UNFSGetCreds) {
294 afs_osi_Sleep((void *)au);
297 *outexporter = (struct afs_exporter *)np;
298 afs_PutUser(au, WRITE_LOCK);
299 /* ReleaseWriteLock(&afs_xnfsreq); */
304 afs_nfsclient_getcreds(struct unixuser *au)
306 struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
307 struct rx_securityClass *csec;
308 struct rx_connection *tconn;
309 struct tokenUnion *tokenPtr;
310 struct rkxadToken *token;
311 SysNameList tsysnames;
316 int code, i, cellnum;
318 au->states |= UNFSGetCreds;
319 memset(&tcreds, 0, sizeof(tcreds));
320 memset(&tsysnames, 0, sizeof(tsysnames));
322 /* Get a connection */
323 /* This sucks a little. We should cache the connections or something.
324 * But at this point I don't yet think it's worth the effort.
326 csec = rxnull_NewClientSecurityObject();
328 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
331 /* Get the sysname, if needed */
332 if (!np->sysnamecount) {
334 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
337 tsysnames.SysNameList_len <= 0 ||
338 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
341 for(i = 0; i < np->sysnamecount; i++)
342 afs_osi_Free(np->sysname[i], MAXSYSNAME);
344 np->sysnamecount = tsysnames.SysNameList_len;
345 for(i = 0; i < np->sysnamecount; i++)
346 np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
347 afs_osi_Free(tsysnames.SysNameList_val,
348 tsysnames.SysNameList_len * sizeof(SysNameEnt));
351 /* Get credentials */
353 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
358 /* Now, set the credentials they gave us... */
359 for (i = 0; i < tcreds.CredInfos_len; i++) {
360 tcred = &tcreds.CredInfos_val[i];
362 /* Find the cell. If it is unknown to us, punt this entry. */
363 tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
364 afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
366 memset(tcred->ct.HandShakeKey, 0, 8);
367 memset(tcred->st.st_val, 0, tcred->st.st_len);
368 afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
371 cellnum = tcell->cellNum;
372 afs_PutCell(tcell, READ_LOCK);
374 /* Find the appropriate unixuser. This might be the same as
375 * the one we were passed (au), but that's OK.
377 tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
378 if (!(tu->exporter)) { /* Created new unixuser struct */
379 np->refCount++; /* so it won't disappear */
380 tu->exporter = (struct afs_exporter *)np;
383 afs_FreeTokens(&tu->tokens);
385 /* Add a new rxkad token. Using the afs_AddRxkadToken interface
386 * would require another copy, so we do this the hard way */
387 tokenptr = afs_AddToken(&tu->tokens, 2);
388 token = &tokenptr->rxkad;
389 token->ticket = tcred->st.st_val;
390 token->ticketLen = tcred->st.st_len;
392 /* copy the clear token */
393 memset(&token->clearToken, 0, sizeof(token->clearToken));
394 memcpy(token->clearToken.HandShakeKey, tcred->ct.HandShakeKey, 8);
395 memset(tcred->ct.HandShakeKey, 0, 8);
396 token->clearToken.AuthHandle = tcred->ct.AuthHandle;
397 token->clearToken.ViceId = tcred->ct.ViceId;
398 token->clearToken.BeginTimestamp = tcred->ct.BeginTimestamp;
399 token->clearToken.EndTimestamp = tcred->ct.EndTimestamp;
401 /* Set everything else, reset connections, and move on. */
402 tu->vid = tcred->vid;
403 tu->states |= UHasTokens;
404 tu->states &= ~UTokensBad;
405 afs_SetPrimary(tu, !!(tcred->states & UPrimary));
406 tu->tokenTime = osi_Time();
407 afs_ResetUserConns(tu);
408 afs_PutUser(tu, WRITE_LOCK);
410 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
414 rx_DestroyConnection(tconn);
416 au->states &= ~UNFSGetCreds;
417 afs_osi_Wakeup((void *)au);
421 /* It's called whenever a new unixuser structure is created for the remote
422 * user associated with the nfsclientpag structure, np */
424 afs_nfsclient_hold(struct nfsclientpag *np)
426 #if defined(AFS_SGIMP_ENV)
427 osi_Assert(ISAFS_GLOCK());
429 AFS_STATCNT(afs_nfsclient_hold);
434 /* check if this exporter corresponds to the specified host */
436 afs_nfsclient_checkhost(struct nfsclientpag *np, afs_uint32 host)
438 if (np->type != EXP_NFS)
440 return np->host == host;
444 /* get the host for this exporter, or 0 if there is an error */
446 afs_nfsclient_gethost(struct nfsclientpag *np)
448 if (np->type != EXP_NFS)
454 /* if inname is non-null, a new system name value is set for the remote
455 * user (inname contains the new sysname). In all cases, outname returns
456 * the current sysname value for this remote user */
458 afs_nfsclient_sysname(struct nfsclientpag *np, char *inname,
459 char ***outname, int *num, int allpags)
461 struct nfsclientpag *tnp;
465 #if defined(AFS_SGIMP_ENV)
466 osi_Assert(ISAFS_GLOCK());
468 AFS_STATCNT(afs_nfsclient_sysname);
470 /* update every client, not just the one making the request */
472 ObtainWriteLock(&afs_xnfspag, 315);
473 for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
474 if (tnp != np && tnp->host == np->host)
475 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
477 ReleaseWriteLock(&afs_xnfspag);
480 for(count=0; count < np->sysnamecount;++count) {
481 afs_osi_Free(np->sysname[count], MAXSYSNAME);
482 np->sysname[count] = NULL;
484 for(count=0; count < *num;++count) {
485 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
488 for(count=0; count < *num;++count) {
490 memcpy(np->sysname[count], cp, t+1); /* include null */
493 np->sysnamecount = *num;
496 /* Don't touch our arguments when called recursively */
497 *outname = np->sysname;
498 *num = np->sysnamecount;
500 return ENODEV; /* XXX */
506 /* Garbage collect routine for the nfs exporter. When pag is -1 then all
507 * entries are removed (used by the nfsclient_shutdown routine); else if
508 * it's non zero then only the entry with that pag is removed, else all
509 * "timedout" entries are removed. TimedOut entries are those who have no
510 * "unixuser" structures associated with them (i.e. unixusercnt == 0) and
511 * they haven't had any activity the last NFSCLIENTGC seconds */
513 afs_nfsclient_GC(struct afs_exporter *exporter,
516 struct nfsclientpag *np, **tnp, *nnp;
517 afs_int32 i, delflag;
520 #if defined(AFS_SGIMP_ENV)
521 osi_Assert(ISAFS_GLOCK());
523 AFS_STATCNT(afs_nfsclient_GC);
524 ObtainWriteLock(&afs_xnfspag, 316);
525 for (i = 0; i < NNFSCLIENTS; i++) {
526 for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
529 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
531 if ((pag == -1) || (!pag && delflag)
532 || (pag && (np->refCount == 0) && (np->pag == pag))) {
534 for(count=0; count < np->sysnamecount;++count) {
535 afs_osi_Free(np->sysname[count], MAXSYSNAME);
537 afs_osi_Free(np, sizeof(struct nfsclientpag));
543 ReleaseWriteLock(&afs_xnfspag);
548 afs_nfsclient_stats(struct afs_exporter *export)
550 /* Nothing much to do here yet since most important stats are collected
551 * directly in the afs_exporter structure itself */
552 AFS_STATCNT(afs_nfsclient_stats);
557 /* This is exposed so that vop_fid can test it, even if iauth is not
560 extern int afs_iauth_initd;
563 #ifdef AFS_AIX_IAUTH_ENV
564 char *afs_nfs_id = "AFSNFSTRANS";
565 /* afs_iauth_verify is the AFS authenticator for NFS.
570 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
571 afs_ucred_t *credp, struct exportinfo *exp)
574 struct nfsclientpag *nfs_pag;
576 struct afs_exporter *outexporter = 0;
579 /* Still needs basic test to see if exporter is on. And need to check the
580 * whole no submounts bit.
584 return 0; /* not us. */
586 /* Only care if it's AFS */
587 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
593 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
594 &dummypag, &outexporter);
595 if (!code && outexporter)
596 EXP_RELE(outexporter);
599 /* ensure anonymous cred. */
600 afs_set_cr_uid(credp, (uid_t) -2; /* anonymous */
601 afs_set_cr_ruid(credp, (uid_t) -2;
604 /* Mark this thread as an NFS translator thread. */
605 afs_set_cr_rgid(credp, NFSXLATOR_CRED);
611 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
612 * and -1 on failure. Can fail because DFS has already registered.
615 afs_iauth_register(void)
617 if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
625 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
628 afs_iauth_unregister(void)
631 nfs_iauth_unregister((unsigned long)afs_nfs_id);
634 #endif /* AFS_AIX_IAUTH_ENV */
639 shutdown_nfsclnt(void)
641 #if defined(AFS_SGIMP_ENV)
642 osi_Assert(ISAFS_GLOCK());
644 AFS_STATCNT(afs_nfsclient_shutdown);
645 #ifdef AFS_AIX_IAUTH_ENV
646 afs_iauth_register();
648 afs_nfsclient_GC(afs_nfsexporter, -1);
649 init_nfsexporter = 0;
651 #endif /* AFS_NONFSTRANS */