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_int32 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(register afs_int32 uid, register afs_int32 host)
52 register struct nfsclientpag *np;
53 register afs_int32 i, now;
55 #if defined(AFS_SGIMP_ENV)
56 osi_Assert(ISAFS_GLOCK());
58 AFS_STATCNT(afs_GetNfsClientPag);
61 MObtainWriteLock(&afs_xnfspag, 314);
62 for (np = afs_nfspags[i]; np; np = np->next) {
63 if (np->uid == uid && np->host == host) {
66 MReleaseWriteLock(&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 MReleaseWriteLock(&afs_xnfspag);
79 np = (struct nfsclientpag *)afs_osi_Alloc(sizeof(struct nfsclientpag));
80 memset((char *)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 MReleaseWriteLock(&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 register 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_int32 host, afs_int32 pag)
114 register struct nfsclientpag *np;
115 register afs_int32 i;
117 #if defined(AFS_SGIMP_ENV)
118 osi_Assert(ISAFS_GLOCK());
120 AFS_STATCNT(afs_FindNfsClientPag);
122 MObtainWriteLock(&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 MReleaseWriteLock(&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 MReleaseWriteLock(&afs_xnfspag);
144 MReleaseWriteLock(&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_int32 host, afs_int32 *pagparam,
179 struct afs_exporter **outexporter)
181 register struct nfsclientpag *np, *tnp;
182 extern struct unixuser *afs_FindUser(), *afs_GetUser();
183 register 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 = (*cred)->cr_uid;
208 /* Do this early, so pag management knows */
210 (*cred)->cr_ruid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
212 (*cred)->cr_rgid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
214 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
216 } else if (pag != NOPAG) {
217 /* Do some minimal pag verification */
218 if (pag > getpag()) {
219 pag = NOPAG; /* treat it as not paged since couldn't be good */
221 if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
224 afs_PutUser(au, READ_LOCK);
228 pag = NOPAG; /* No unixuser struct so pag not trusted */
231 np = afs_FindNfsClientPag(uid, host, 0);
232 afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
233 ICL_TYPE_LONG, (*cred)->cr_uid, ICL_TYPE_INT32, host,
234 ICL_TYPE_POINTER, np);
235 /* If remote-pags are enabled, we are no longer interested in what PAG
236 * they claimed, and from here on we should behave as if they claimed
237 * none at all, which is to say we use the (local) pag named in the
238 * nfsclientpag structure (if any). This is deferred until here so
239 * that we can log the PAG they claimed.
241 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
244 /* Even if there is a "good" pag coming in we don't accept it if no
245 * nfsclientpag struct exists for the user since that would mean
246 * that the translator rebooted and therefore we ignore all older
250 if (code = setpag(u.u_procp, cred, -1, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
252 if ((code = setpag(cred, -1, &pag, 0))) {
255 afs_PutUser(au, READ_LOCK);
256 /* ReleaseWriteLock(&afs_xnfsreq); */
257 #if defined(KERNEL_HAVE_UERROR)
262 np = afs_GetNfsClientPag(uid, host);
264 np->client_uid = (*cred)->cr_uid;
268 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
270 if ((code = setpag(cred, np->pag, &pag, 0))) {
272 afs_PutNfsClientPag(np);
273 /* ReleaseWriteLock(&afs_xnfsreq); */
274 #if defined(KERNEL_HAVE_UERROR)
279 } else if (au->exporter
280 && ((struct afs_exporter *)np != au->exporter)) {
281 tnp = (struct nfsclientpag *)au->exporter;
282 if (tnp->uid && (tnp->uid != (afs_int32) - 2)) { /* allow "root" initiators */
283 /* Pag doesn't belong to caller; treat it as an unpaged call too */
285 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
287 if ((code = setpag(cred, np->pag, &pag, 0))) {
289 afs_PutNfsClientPag(np);
290 afs_PutUser(au, READ_LOCK);
291 /* ReleaseWriteLock(&afs_xnfsreq); */
292 #if defined(KERNEL_HAVE_UERROR)
297 afs_nfsexporter->exp_stats.invalidpag++;
302 afs_PutUser(au, READ_LOCK);
303 au = afs_GetUser(pag, -1, WRITE_LOCK);
304 if (!(au->exporter)) { /* Created new unixuser struct */
305 np->refCount++; /* so it won't disappear */
306 au->exporter = (struct afs_exporter *)np;
307 if ((afs_nfsexporter->exp_states & EXP_CALLBACK))
308 afs_nfsclient_getcreds(au);
309 } else while (au->states & UNFSGetCreds) {
310 afs_osi_Sleep((void *)au);
313 *outexporter = (struct afs_exporter *)np;
314 afs_PutUser(au, WRITE_LOCK);
315 /* ReleaseWriteLock(&afs_xnfsreq); */
320 afs_nfsclient_getcreds(au)
323 struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
324 struct rx_securityClass *csec;
325 struct rx_connection *tconn;
326 SysNameList tsysnames;
331 int code, i, cellnum;
333 au->states |= UNFSGetCreds;
334 memset(&tcreds, 0, sizeof(tcreds));
335 memset(&tsysnames, 0, sizeof(tsysnames));
337 /* Get a connection */
338 /* This sucks a little. We should cache the connections or something.
339 * But at this point I don't yet think it's worth the effort.
341 csec = rxnull_NewClientSecurityObject();
343 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
346 /* Get the sysname, if needed */
347 if (!np->sysnamecount) {
349 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
352 tsysnames.SysNameList_len <= 0 ||
353 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
356 for(i = 0; i < np->sysnamecount; i++)
357 afs_osi_Free(np->sysname[i], MAXSYSNAME);
359 np->sysnamecount = tsysnames.SysNameList_len;
360 for(i = 0; i < np->sysnamecount; i++)
361 np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
362 afs_osi_Free(tsysnames.SysNameList_val,
363 tsysnames.SysNameList_len * sizeof(SysNameEnt));
366 /* Get credentials */
368 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
373 /* Now, set the credentials they gave us... */
374 for (i = 0; i < tcreds.CredInfos_len; i++) {
375 tcred = &tcreds.CredInfos_val[i];
377 /* Find the cell. If it is unknown to us, punt this entry. */
378 tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
379 afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
381 memset(tcred->ct.HandShakeKey, 0, 8);
382 memset(tcred->st.st_val, 0, tcred->st.st_len);
383 afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
386 cellnum = tcell->cellNum;
387 afs_PutCell(tcell, READ_LOCK);
389 /* Find the appropriate unixuser. This might be the same as
390 * the one we were passed (au), but that's OK.
392 tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
393 if (!(tu->exporter)) { /* Created new unixuser struct */
394 np->refCount++; /* so it won't disappear */
395 tu->exporter = (struct afs_exporter *)np;
398 /* free any old secret token, and keep the new one */
399 if (tu->stp != NULL) {
400 afs_osi_Free(tu->stp, tu->stLen);
402 tu->stp = tcred->st.st_val;
403 tu->stLen = tcred->st.st_len;
405 /* copy the clear token */
406 memset(&tu->ct, 0, sizeof(tu->ct));
407 memcpy(tu->ct.HandShakeKey, tcred->ct.HandShakeKey, 8);
408 memset(tcred->ct.HandShakeKey, 0, 8);
409 tu->ct.AuthHandle = tcred->ct.AuthHandle;
410 tu->ct.ViceId = tcred->ct.ViceId;
411 tu->ct.BeginTimestamp = tcred->ct.BeginTimestamp;
412 tu->ct.EndTimestamp = tcred->ct.EndTimestamp;
414 /* Set everything else, reset connections, and move on. */
415 tu->vid = tcred->vid;
416 tu->states |= UHasTokens;
417 tu->states &= ~UTokensBad;
418 afs_SetPrimary(tu, !!(tcred->states & UPrimary));
419 tu->tokenTime = osi_Time();
420 afs_ResetUserConns(tu);
421 afs_PutUser(tu, WRITE_LOCK);
423 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
427 rx_DestroyConnection(tconn);
429 au->states &= ~UNFSGetCreds;
430 afs_osi_Wakeup((void *)au);
434 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
436 afs_nfsclient_hold(np)
437 register struct nfsclientpag *np;
439 #if defined(AFS_SGIMP_ENV)
440 osi_Assert(ISAFS_GLOCK());
442 AFS_STATCNT(afs_nfsclient_hold);
447 /* check if this exporter corresponds to the specified host */
449 afs_nfsclient_checkhost(np, host)
450 register struct nfsclientpag *np;
452 if (np->type != EXP_NFS)
454 return np->host == host;
458 /* get the host for this exporter, or 0 if there is an error */
460 afs_nfsclient_gethost(np)
461 register struct nfsclientpag *np;
463 if (np->type != EXP_NFS)
469 /* if inname is non-null, a new system name value is set for the remote user (inname contains the new sysname). In all cases, outname returns the current sysname value for this remote user */
471 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname,
472 char ***outname, int *num, int allpags)
474 register struct nfsclientpag *tnp;
475 register afs_int32 i;
478 #if defined(AFS_SGIMP_ENV)
479 osi_Assert(ISAFS_GLOCK());
481 AFS_STATCNT(afs_nfsclient_sysname);
483 /* update every client, not just the one making the request */
485 MObtainWriteLock(&afs_xnfspag, 315);
486 for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
487 if (tnp != np && tnp->host == np->host)
488 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
490 MReleaseWriteLock(&afs_xnfspag);
493 for(count=0; count < np->sysnamecount;++count) {
494 afs_osi_Free(np->sysname[count], MAXSYSNAME);
495 np->sysname[count] = NULL;
497 for(count=0; count < *num;++count) {
498 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
501 for(count=0; count < *num;++count) {
503 memcpy(np->sysname[count], cp, t+1); /* include null */
506 np->sysnamecount = *num;
509 /* Don't touch our arguments when called recursively */
510 *outname = np->sysname;
511 *num = np->sysnamecount;
513 return ENODEV; /* XXX */
519 /* Garbage collect routine for the nfs exporter. When pag is -1 then all entries are removed (used by the nfsclient_shutdown routine); else if it's non zero then only the entry with that pag is removed, else all "timedout" entries are removed. TimedOut entries are those who have no "unixuser" structures associated with them (i.e. unixusercnt == 0) and they haven't had any activity the last NFSCLIENTGC seconds */
521 afs_nfsclient_GC(exporter, pag)
522 register struct afs_exporter *exporter;
523 register afs_int32 pag;
525 register struct nfsclientpag *np, **tnp, *nnp;
526 register afs_int32 i, delflag;
529 #if defined(AFS_SGIMP_ENV)
530 osi_Assert(ISAFS_GLOCK());
532 AFS_STATCNT(afs_nfsclient_GC);
533 MObtainWriteLock(&afs_xnfspag, 316);
534 for (i = 0; i < NNFSCLIENTS; i++) {
535 for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
538 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
540 if ((pag == -1) || (!pag && delflag)
541 || (pag && (np->refCount == 0) && (np->pag == pag))) {
543 for(count=0; count < np->sysnamecount;++count) {
544 afs_osi_Free(np->sysname[count], MAXSYSNAME);
546 afs_osi_Free(np, sizeof(struct nfsclientpag));
552 MReleaseWriteLock(&afs_xnfspag);
557 afs_nfsclient_stats(register struct afs_exporter *export)
559 /* Nothing much to do here yet since most important stats are collected
560 * directly in the afs_exporter structure itself */
561 AFS_STATCNT(afs_nfsclient_stats);
566 /* This is exposed so that vop_fid can test it, even if iauth is not
569 extern int afs_iauth_initd;
572 #ifdef AFS_AIX_IAUTH_ENV
573 char *afs_nfs_id = "AFSNFSTRANS";
574 /* afs_iauth_verify is the AFS authenticator for NFS.
579 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
580 AFS_UCRED *credp, struct exportinfo *exp)
583 struct nfsclientpag *nfs_pag;
585 struct afs_exporter *outexporter = 0;
588 /* Still needs basic test to see if exporter is on. And need to check the
589 * whole no submounts bit.
593 return 0; /* not us. */
595 /* Only care if it's AFS */
596 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
602 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
603 &dummypag, &outexporter);
604 if (!code && outexporter)
605 EXP_RELE(outexporter);
608 /* ensure anonymous cred. */
609 credp->cr_uid = credp->cr_ruid = (uid_t) - 2; /* anonymous */
612 /* Mark this thread as an NFS translator thread. */
613 credp->cr_rgid = NFSXLATOR_CRED;
619 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
620 * and -1 on failure. Can fail because DFS has already registered.
625 if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
633 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
636 afs_iauth_unregister()
639 nfs_iauth_unregister((unsigned long)afs_nfs_id);
642 #endif /* AFS_AIX_IAUTH_ENV */
649 #if defined(AFS_SGIMP_ENV)
650 osi_Assert(ISAFS_GLOCK());
652 AFS_STATCNT(afs_nfsclient_shutdown);
653 #ifdef AFS_AIX_IAUTH_ENV
654 afs_iauth_register();
656 afs_nfsclient_GC(afs_nfsexporter, -1);
657 init_nfsexporter = 0;
659 #endif /* AFS_NONFSTRANS */