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"
16 #if !defined(AFS_NONFSTRANS) || defined(AFS_AIX_IAUTH_ENV)
17 #include "afs/sysincludes.h" /* Standard vendor system headers */
18 #include "afsincludes.h" /* Afs-based standard headers */
19 #include "afs/afs_stats.h" /* statistics */
20 #include "afs/nfsclient.h"
21 #include "rx/rx_globals.h"
22 #include "afs/pagcb.h"
24 void afs_nfsclient_hold(), afs_PutNfsClientPag(), afs_nfsclient_GC();
25 static void afs_nfsclient_getcreds();
26 int afs_nfsclient_sysname(), afs_nfsclient_stats(), afs_nfsclient_checkhost();
27 afs_int32 afs_nfsclient_gethost();
28 #ifdef AFS_AIX_IAUTH_ENV
29 int afs_allnfsreqs, afs_nfscalls;
32 /* routines exported to the "AFS exporter" layer */
33 struct exporterops nfs_exportops = {
34 afs_nfsclient_reqhandler,
36 afs_PutNfsClientPag, /* Used to be afs_nfsclient_rele */
37 afs_nfsclient_sysname,
40 afs_nfsclient_checkhost,
45 struct nfsclientpag *afs_nfspags[NNFSCLIENTS];
46 afs_lock_t afs_xnfspag /*, afs_xnfsreq */ ;
47 extern struct afs_exporter *afs_nfsexporter;
49 /* Creates an nfsclientpag structure for the (uid, host) pair if one doesn't exist. RefCount is incremented and it's time stamped. */
50 static struct nfsclientpag *
51 afs_GetNfsClientPag(uid, host)
52 register afs_int32 uid, host;
54 register struct nfsclientpag *np;
55 register afs_int32 i, now;
57 #if defined(AFS_SGIMP_ENV)
58 osi_Assert(ISAFS_GLOCK());
60 AFS_STATCNT(afs_GetNfsClientPag);
63 MObtainWriteLock(&afs_xnfspag, 314);
64 for (np = afs_nfspags[i]; np; np = np->next) {
65 if (np->uid == uid && np->host == host) {
68 MReleaseWriteLock(&afs_xnfspag);
72 /* next try looking for NOPAG dude, if we didn't find an exact match */
73 for (np = afs_nfspags[i]; np; np = np->next) {
74 if (np->uid == NOPAG && np->host == host) {
77 MReleaseWriteLock(&afs_xnfspag);
81 np = (struct nfsclientpag *)afs_osi_Alloc(sizeof(struct nfsclientpag));
82 memset((char *)np, 0, sizeof(struct nfsclientpag));
83 /* Copy the necessary afs_exporter fields */
84 memcpy((char *)np, (char *)afs_nfsexporter, sizeof(struct afs_exporter));
85 np->next = afs_nfspags[i];
91 MReleaseWriteLock(&afs_xnfspag);
96 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
97 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
99 afs_PutNfsClientPag(np)
100 register struct nfsclientpag *np;
102 #if defined(AFS_SGIMP_ENV)
103 osi_Assert(ISAFS_GLOCK());
105 AFS_STATCNT(afs_PutNfsClientPag);
110 /* Return the nfsclientpag structure associated with the (uid, host) or {pag, host} pair, if pag is nonzero. RefCount is incremented and it's time stamped. */
111 static struct nfsclientpag *
112 afs_FindNfsClientPag(uid, host, pag)
113 register afs_int32 uid, host, pag;
115 register struct nfsclientpag *np;
116 register afs_int32 i;
118 #if defined(AFS_SGIMP_ENV)
119 osi_Assert(ISAFS_GLOCK());
121 AFS_STATCNT(afs_FindNfsClientPag);
123 MObtainWriteLock(&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 MReleaseWriteLock(&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 MReleaseWriteLock(&afs_xnfspag);
145 MReleaseWriteLock(&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,
178 struct AFS_UCRED **cred,
179 afs_int32 host, afs_int32 *pagparam,
180 struct afs_exporter **outexporter)
182 register struct nfsclientpag *np, *tnp;
183 extern struct unixuser *afs_FindUser(), *afs_GetUser();
184 register 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'. Set/Reset via a pioctl call (fs exportafs). Note that this is on top of the /etc/exports nfs requirement (i.e. /afs must be exported to all or whomever there too!)
196 afs_nfsexporter->exp_stats.rejectedcalls++;
199 /* ObtainWriteLock(&afs_xnfsreq); */
200 pag = PagInCred(*cred);
201 #if defined(AFS_SUN510_ENV)
202 uid = crgetuid(*cred);
204 uid = (*cred)->cr_uid;
206 /* Do this early, so pag management knows */
208 (*cred)->cr_ruid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
210 (*cred)->cr_rgid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
212 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
214 } else if (pag != NOPAG) {
215 /* Do some minimal pag verification */
216 if (pag > getpag()) {
217 pag = NOPAG; /* treat it as not paged since couldn't be good */
219 if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
222 afs_PutUser(au, READ_LOCK);
226 pag = NOPAG; /* No unixuser struct so pag not trusted */
229 np = afs_FindNfsClientPag(uid, host, 0);
230 afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
231 ICL_TYPE_LONG, (*cred)->cr_uid, ICL_TYPE_INT32, host,
232 ICL_TYPE_POINTER, np);
233 /* If remote-pags are enabled, we are no longer interested in what PAG
234 * they claimed, and from here on we should behave as if they claimed
235 * none at all, which is to say we use the (local) pag named in the
236 * nfsclientpag structure (if any). This is deferred until here so
237 * that we can log the PAG they claimed.
239 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
242 /* Even if there is a "good" pag coming in we don't accept it if no nfsclientpag struct exists for the user since that would mean that the translator rebooted and therefore we ignore all older pag values */
244 if (code = setpag(u.u_procp, cred, -1, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
246 if ((code = setpag(cred, -1, &pag, 0))) {
249 afs_PutUser(au, READ_LOCK);
250 /* ReleaseWriteLock(&afs_xnfsreq); */
251 #if defined(KERNEL_HAVE_UERROR)
256 np = afs_GetNfsClientPag(uid, host);
258 np->client_uid = (*cred)->cr_uid;
262 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
264 if ((code = setpag(cred, np->pag, &pag, 0))) {
266 afs_PutNfsClientPag(np);
267 /* ReleaseWriteLock(&afs_xnfsreq); */
268 #if defined(KERNEL_HAVE_UERROR)
273 } else if (au->exporter
274 && ((struct afs_exporter *)np != au->exporter)) {
275 tnp = (struct nfsclientpag *)au->exporter;
276 if (tnp->uid && (tnp->uid != (afs_int32) - 2)) { /* allow "root" initiators */
277 /* Pag doesn't belong to caller; treat it as an unpaged call too */
279 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
281 if ((code = setpag(cred, np->pag, &pag, 0))) {
283 afs_PutNfsClientPag(np);
284 afs_PutUser(au, READ_LOCK);
285 /* ReleaseWriteLock(&afs_xnfsreq); */
286 #if defined(KERNEL_HAVE_UERROR)
291 afs_nfsexporter->exp_stats.invalidpag++;
296 afs_PutUser(au, READ_LOCK);
297 au = afs_GetUser(pag, -1, WRITE_LOCK);
298 if (!(au->exporter)) { /* Created new unixuser struct */
299 np->refCount++; /* so it won't disappear */
300 au->exporter = (struct afs_exporter *)np;
301 if ((afs_nfsexporter->exp_states & EXP_CALLBACK))
302 afs_nfsclient_getcreds(au);
303 } else while (au->states & UNFSGetCreds) {
304 afs_osi_Sleep((void *)au);
307 *outexporter = (struct afs_exporter *)np;
308 afs_PutUser(au, WRITE_LOCK);
309 /* ReleaseWriteLock(&afs_xnfsreq); */
314 afs_nfsclient_getcreds(au)
317 struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
318 struct rx_securityClass *csec;
319 struct rx_connection *tconn;
320 SysNameList tsysnames;
325 int code, i, cellnum;
327 au->states |= UNFSGetCreds;
328 memset(&tcreds, 0, sizeof(tcreds));
329 memset(&tsysnames, 0, sizeof(tsysnames));
331 /* Get a connection */
332 /* This sucks a little. We should cache the connections or something.
333 * But at this point I don't yet think it's worth the effort.
335 csec = rxnull_NewClientSecurityObject();
337 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
340 /* Get the sysname, if needed */
341 if (!np->sysnamecount) {
343 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
346 tsysnames.SysNameList_len <= 0 ||
347 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
350 for(i = 0; i < np->sysnamecount; i++)
351 afs_osi_Free(np->sysname[i], MAXSYSNAME);
353 np->sysnamecount = tsysnames.SysNameList_len;
354 for(i = 0; i < np->sysnamecount; i++)
355 np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
356 afs_osi_Free(tsysnames.SysNameList_val,
357 tsysnames.SysNameList_len * sizeof(SysNameEnt));
360 /* Get credentials */
362 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
367 /* Now, set the credentials they gave us... */
368 for (i = 0; i < tcreds.CredInfos_len; i++) {
369 tcred = &tcreds.CredInfos_val[i];
371 /* Find the cell. If it is unknown to us, punt this entry. */
372 tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
373 afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
375 memset(tcred->ct.HandShakeKey, 0, 8);
376 memset(tcred->st.st_val, 0, tcred->st.st_len);
377 afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
380 cellnum = tcell->cellNum;
381 afs_PutCell(tcell, READ_LOCK);
383 /* Find the appropriate unixuser. This might be the same as
384 * the one we were passed (au), but that's OK.
386 tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
387 if (!(tu->exporter)) { /* Created new unixuser struct */
388 np->refCount++; /* so it won't disappear */
389 tu->exporter = (struct afs_exporter *)np;
392 /* free any old secret token, and keep the new one */
393 if (tu->stp != NULL) {
394 afs_osi_Free(tu->stp, tu->stLen);
396 tu->stp = tcred->st.st_val;
397 tu->stLen = tcred->st.st_len;
399 /* copy the clear token */
400 memset(&tu->ct, 0, sizeof(tu->ct));
401 memcpy(tu->ct.HandShakeKey, tcred->ct.HandShakeKey, 8);
402 memset(tcred->ct.HandShakeKey, 0, 8);
403 tu->ct.AuthHandle = tcred->ct.AuthHandle;
404 tu->ct.ViceId = tcred->ct.ViceId;
405 tu->ct.BeginTimestamp = tcred->ct.BeginTimestamp;
406 tu->ct.EndTimestamp = tcred->ct.EndTimestamp;
408 /* Set everything else, reset connections, and move on. */
409 tu->vid = tcred->vid;
410 tu->states |= UHasTokens;
411 tu->states &= ~UTokensBad;
412 afs_SetPrimary(tu, !!(tcred->states & UPrimary));
413 tu->tokenTime = osi_Time();
414 afs_ResetUserConns(tu);
415 afs_PutUser(tu, WRITE_LOCK);
417 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
421 rx_DestroyConnection(tconn);
423 au->states &= ~UNFSGetCreds;
424 afs_osi_Wakeup((void *)au);
428 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
430 afs_nfsclient_hold(np)
431 register struct nfsclientpag *np;
433 #if defined(AFS_SGIMP_ENV)
434 osi_Assert(ISAFS_GLOCK());
436 AFS_STATCNT(afs_nfsclient_hold);
441 /* check if this exporter corresponds to the specified host */
443 afs_nfsclient_checkhost(np, host)
444 register struct nfsclientpag *np;
446 if (np->type != EXP_NFS)
448 return np->host == host;
452 /* get the host for this exporter, or 0 if there is an error */
454 afs_nfsclient_gethost(np)
455 register struct nfsclientpag *np;
457 if (np->type != EXP_NFS)
463 /* 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 */
465 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname,
466 char ***outname, int *num, int allpags)
468 register struct nfsclientpag *tnp;
469 register afs_int32 i;
472 #if defined(AFS_SGIMP_ENV)
473 osi_Assert(ISAFS_GLOCK());
475 AFS_STATCNT(afs_nfsclient_sysname);
477 /* update every client, not just the one making the request */
479 MObtainWriteLock(&afs_xnfspag, 315);
480 for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
481 if (tnp != np && tnp->host == np->host)
482 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
484 MReleaseWriteLock(&afs_xnfspag);
487 for(count=0; count < np->sysnamecount;++count) {
488 afs_osi_Free(np->sysname[count], MAXSYSNAME);
489 np->sysname[count] = NULL;
491 for(count=0; count < *num;++count) {
492 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
495 for(count=0; count < *num;++count) {
497 memcpy(np->sysname[count], cp, t+1); /* include null */
500 np->sysnamecount = *num;
503 /* Don't touch our arguments when called recursively */
504 *outname = np->sysname;
505 *num = np->sysnamecount;
507 return ENODEV; /* XXX */
513 /* 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 */
515 afs_nfsclient_GC(exporter, pag)
516 register struct afs_exporter *exporter;
517 register afs_int32 pag;
519 register struct nfsclientpag *np, **tnp, *nnp;
520 register afs_int32 i, delflag;
523 #if defined(AFS_SGIMP_ENV)
524 osi_Assert(ISAFS_GLOCK());
526 AFS_STATCNT(afs_nfsclient_GC);
527 MObtainWriteLock(&afs_xnfspag, 316);
528 for (i = 0; i < NNFSCLIENTS; i++) {
529 for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
532 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
534 if ((pag == -1) || (!pag && delflag)
535 || (pag && (np->refCount == 0) && (np->pag == pag))) {
537 for(count=0; count < np->sysnamecount;++count) {
538 afs_osi_Free(np->sysname[count], MAXSYSNAME);
540 afs_osi_Free(np, sizeof(struct nfsclientpag));
546 MReleaseWriteLock(&afs_xnfspag);
551 afs_nfsclient_stats(export)
552 register struct afs_exporter *export;
554 /* Nothing much to do here yet since most important stats are collected directly in the afs_exporter structure itself */
555 AFS_STATCNT(afs_nfsclient_stats);
560 /* This is exposed so that vop_fid can test it, even if iauth is not
563 extern int afs_iauth_initd;
566 #ifdef AFS_AIX_IAUTH_ENV
567 char *afs_nfs_id = "AFSNFSTRANS";
568 /* afs_iauth_verify is the AFS authenticator for NFS.
573 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
574 struct AFS_UCRED *credp, struct exportinfo *exp)
577 struct nfsclientpag *nfs_pag;
579 struct afs_exporter *outexporter = 0;
582 /* Still needs basic test to see if exporter is on. And need to check the
583 * whole no submounts bit.
587 return 0; /* not us. */
589 /* Only care if it's AFS */
590 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
596 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
597 &dummypag, &outexporter);
598 if (!code && outexporter)
599 EXP_RELE(outexporter);
602 /* ensure anonymous cred. */
603 credp->cr_uid = credp->cr_ruid = (uid_t) - 2; /* anonymous */
606 /* Mark this thread as an NFS translator thread. */
607 credp->cr_rgid = NFSXLATOR_CRED;
613 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
614 * and -1 on failure. Can fail because DFS has already registered.
619 if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
627 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
630 afs_iauth_unregister()
633 nfs_iauth_unregister((unsigned long)afs_nfs_id);
636 #endif /* AFS_AIX_IAUTH_ENV */
643 #if defined(AFS_SGIMP_ENV)
644 osi_Assert(ISAFS_GLOCK());
646 AFS_STATCNT(afs_nfsclient_shutdown);
647 #ifdef AFS_AIX_IAUTH_ENV
648 afs_iauth_register();
650 afs_nfsclient_GC(afs_nfsexporter, -1);
651 init_nfsexporter = 0;
653 #endif /* AFS_NONFSTRANS */