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
50 * exist. RefCount is incremented and it's time stamped. */
51 static struct nfsclientpag *
52 afs_GetNfsClientPag(register afs_int32 uid, register afs_int32 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
111 * {pag, host} pair, if pag is nonzero. RefCount is incremented and it's
113 static struct nfsclientpag *
114 afs_FindNfsClientPag(afs_int32 uid, afs_int32 host, afs_int32 pag)
116 register struct nfsclientpag *np;
117 register afs_int32 i;
119 #if defined(AFS_SGIMP_ENV)
120 osi_Assert(ISAFS_GLOCK());
122 AFS_STATCNT(afs_FindNfsClientPag);
124 MObtainWriteLock(&afs_xnfspag, 315);
125 for (np = afs_nfspags[i]; np; np = np->next) {
126 if (np->host == host) {
127 if ((pag && pag == np->pag) || (!pag && (uid == np->uid))) {
129 np->lastcall = osi_Time();
130 MReleaseWriteLock(&afs_xnfspag);
135 /* still not there, try looking for a wildcard dude */
136 for (np = afs_nfspags[i]; np; np = np->next) {
137 if (np->host == host) {
138 if (np->uid == NOPAG) {
140 np->lastcall = osi_Time();
141 MReleaseWriteLock(&afs_xnfspag);
146 MReleaseWriteLock(&afs_xnfspag);
151 /* routine to initialize the exporter, made global so we can call it
154 struct afs_exporter *afs_nfsexported = 0;
155 static afs_int32 init_nfsexporter = 0;
158 afs_nfsclient_init(void)
160 #if defined(AFS_SGIMP_ENV)
161 osi_Assert(ISAFS_GLOCK());
163 if (!init_nfsexporter) {
164 extern struct afs_exporter *exporter_add();
166 init_nfsexporter = 1;
167 LOCK_INIT(&afs_xnfspag, "afs_xnfspag");
169 exporter_add(0, &nfs_exportops, EXP_EXPORTED, EXP_NFS, NULL);
174 /* Main handler routine for the NFS exporter. It's called in the early
175 * phases of any remote call (via the NFS server or pioctl).
178 afs_nfsclient_reqhandler(struct afs_exporter *exporter,
179 struct AFS_UCRED **cred,
180 afs_int32 host, afs_int32 *pagparam,
181 struct afs_exporter **outexporter)
183 register struct nfsclientpag *np, *tnp;
184 extern struct unixuser *afs_FindUser(), *afs_GetUser();
185 register struct unixuser *au = 0;
186 afs_int32 uid, pag, code = 0;
189 AFS_STATCNT(afs_nfsclient_reqhandler);
190 if (!afs_nfsexporter)
191 afs_nfsexporter = afs_nfsexported;
193 afs_nfsexporter->exp_stats.calls++;
194 if (!(afs_nfsexporter->exp_states & EXP_EXPORTED)) {
195 /* No afs requests accepted as long as EXPORTED flag is turned 'off'.
196 * Set/Reset via a pioctl call (fs exportafs). Note that this is on
197 * top of the /etc/exports nfs requirement (i.e. /afs must be
198 * exported to all or whomever there too!)
200 afs_nfsexporter->exp_stats.rejectedcalls++;
203 /* ObtainWriteLock(&afs_xnfsreq); */
204 pag = PagInCred(*cred);
205 #if defined(AFS_SUN510_ENV)
206 uid = crgetuid(*cred);
208 uid = (*cred)->cr_uid;
210 /* Do this early, so pag management knows */
212 (*cred)->cr_ruid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
214 (*cred)->cr_rgid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
216 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
218 } else if (pag != NOPAG) {
219 /* Do some minimal pag verification */
220 if (pag > getpag()) {
221 pag = NOPAG; /* treat it as not paged since couldn't be good */
223 if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
226 afs_PutUser(au, READ_LOCK);
230 pag = NOPAG; /* No unixuser struct so pag not trusted */
233 np = afs_FindNfsClientPag(uid, host, 0);
234 afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
235 ICL_TYPE_LONG, (*cred)->cr_uid, ICL_TYPE_INT32, host,
236 ICL_TYPE_POINTER, np);
237 /* If remote-pags are enabled, we are no longer interested in what PAG
238 * they claimed, and from here on we should behave as if they claimed
239 * none at all, which is to say we use the (local) pag named in the
240 * nfsclientpag structure (if any). This is deferred until here so
241 * that we can log the PAG they claimed.
243 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
246 /* Even if there is a "good" pag coming in we don't accept it if no
247 * nfsclientpag struct exists for the user since that would mean
248 * that the translator rebooted and therefore we ignore all older
252 if (code = setpag(u.u_procp, cred, -1, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
254 if ((code = setpag(cred, -1, &pag, 0))) {
257 afs_PutUser(au, READ_LOCK);
258 /* ReleaseWriteLock(&afs_xnfsreq); */
259 #if defined(KERNEL_HAVE_UERROR)
264 np = afs_GetNfsClientPag(uid, host);
266 np->client_uid = (*cred)->cr_uid;
270 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
272 if ((code = setpag(cred, np->pag, &pag, 0))) {
274 afs_PutNfsClientPag(np);
275 /* ReleaseWriteLock(&afs_xnfsreq); */
276 #if defined(KERNEL_HAVE_UERROR)
281 } else if (au->exporter
282 && ((struct afs_exporter *)np != au->exporter)) {
283 tnp = (struct nfsclientpag *)au->exporter;
284 if (tnp->uid && (tnp->uid != (afs_int32) - 2)) { /* allow "root" initiators */
285 /* Pag doesn't belong to caller; treat it as an unpaged call too */
287 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
289 if ((code = setpag(cred, np->pag, &pag, 0))) {
291 afs_PutNfsClientPag(np);
292 afs_PutUser(au, READ_LOCK);
293 /* ReleaseWriteLock(&afs_xnfsreq); */
294 #if defined(KERNEL_HAVE_UERROR)
299 afs_nfsexporter->exp_stats.invalidpag++;
304 afs_PutUser(au, READ_LOCK);
305 au = afs_GetUser(pag, -1, WRITE_LOCK);
306 if (!(au->exporter)) { /* Created new unixuser struct */
307 np->refCount++; /* so it won't disappear */
308 au->exporter = (struct afs_exporter *)np;
309 if ((afs_nfsexporter->exp_states & EXP_CALLBACK))
310 afs_nfsclient_getcreds(au);
311 } else while (au->states & UNFSGetCreds) {
312 afs_osi_Sleep((void *)au);
315 *outexporter = (struct afs_exporter *)np;
316 afs_PutUser(au, WRITE_LOCK);
317 /* ReleaseWriteLock(&afs_xnfsreq); */
322 afs_nfsclient_getcreds(au)
325 struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
326 struct rx_securityClass *csec;
327 struct rx_connection *tconn;
328 SysNameList tsysnames;
333 int code, i, cellnum;
335 au->states |= UNFSGetCreds;
336 memset(&tcreds, 0, sizeof(tcreds));
337 memset(&tsysnames, 0, sizeof(tsysnames));
339 /* Get a connection */
340 /* This sucks a little. We should cache the connections or something.
341 * But at this point I don't yet think it's worth the effort.
343 csec = rxnull_NewClientSecurityObject();
345 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
348 /* Get the sysname, if needed */
349 if (!np->sysnamecount) {
351 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
354 tsysnames.SysNameList_len <= 0 ||
355 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
358 for(i = 0; i < np->sysnamecount; i++)
359 afs_osi_Free(np->sysname[i], MAXSYSNAME);
361 np->sysnamecount = tsysnames.SysNameList_len;
362 for(i = 0; i < np->sysnamecount; i++)
363 np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
364 afs_osi_Free(tsysnames.SysNameList_val,
365 tsysnames.SysNameList_len * sizeof(SysNameEnt));
368 /* Get credentials */
370 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
375 /* Now, set the credentials they gave us... */
376 for (i = 0; i < tcreds.CredInfos_len; i++) {
377 tcred = &tcreds.CredInfos_val[i];
379 /* Find the cell. If it is unknown to us, punt this entry. */
380 tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
381 afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
383 memset(tcred->ct.HandShakeKey, 0, 8);
384 memset(tcred->st.st_val, 0, tcred->st.st_len);
385 afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
388 cellnum = tcell->cellNum;
389 afs_PutCell(tcell, READ_LOCK);
391 /* Find the appropriate unixuser. This might be the same as
392 * the one we were passed (au), but that's OK.
394 tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
395 if (!(tu->exporter)) { /* Created new unixuser struct */
396 np->refCount++; /* so it won't disappear */
397 tu->exporter = (struct afs_exporter *)np;
400 /* free any old secret token, and keep the new one */
401 if (tu->stp != NULL) {
402 afs_osi_Free(tu->stp, tu->stLen);
404 tu->stp = tcred->st.st_val;
405 tu->stLen = tcred->st.st_len;
407 /* copy the clear token */
408 memset(&tu->ct, 0, sizeof(tu->ct));
409 memcpy(tu->ct.HandShakeKey, tcred->ct.HandShakeKey, 8);
410 memset(tcred->ct.HandShakeKey, 0, 8);
411 tu->ct.AuthHandle = tcred->ct.AuthHandle;
412 tu->ct.ViceId = tcred->ct.ViceId;
413 tu->ct.BeginTimestamp = tcred->ct.BeginTimestamp;
414 tu->ct.EndTimestamp = tcred->ct.EndTimestamp;
416 /* Set everything else, reset connections, and move on. */
417 tu->vid = tcred->vid;
418 tu->states |= UHasTokens;
419 tu->states &= ~UTokensBad;
420 afs_SetPrimary(tu, !!(tcred->states & UPrimary));
421 tu->tokenTime = osi_Time();
422 afs_ResetUserConns(tu);
423 afs_PutUser(tu, WRITE_LOCK);
425 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
429 rx_DestroyConnection(tconn);
431 au->states &= ~UNFSGetCreds;
432 afs_osi_Wakeup((void *)au);
436 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
438 afs_nfsclient_hold(np)
439 register struct nfsclientpag *np;
441 #if defined(AFS_SGIMP_ENV)
442 osi_Assert(ISAFS_GLOCK());
444 AFS_STATCNT(afs_nfsclient_hold);
449 /* check if this exporter corresponds to the specified host */
451 afs_nfsclient_checkhost(np, host)
452 register struct nfsclientpag *np;
454 if (np->type != EXP_NFS)
456 return np->host == host;
460 /* get the host for this exporter, or 0 if there is an error */
462 afs_nfsclient_gethost(np)
463 register struct nfsclientpag *np;
465 if (np->type != EXP_NFS)
471 /* 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 */
473 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname,
474 char ***outname, int *num, int allpags)
476 register struct nfsclientpag *tnp;
477 register afs_int32 i;
480 #if defined(AFS_SGIMP_ENV)
481 osi_Assert(ISAFS_GLOCK());
483 AFS_STATCNT(afs_nfsclient_sysname);
485 /* update every client, not just the one making the request */
487 MObtainWriteLock(&afs_xnfspag, 315);
488 for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
489 if (tnp != np && tnp->host == np->host)
490 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
492 MReleaseWriteLock(&afs_xnfspag);
495 for(count=0; count < np->sysnamecount;++count) {
496 afs_osi_Free(np->sysname[count], MAXSYSNAME);
497 np->sysname[count] = NULL;
499 for(count=0; count < *num;++count) {
500 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
503 for(count=0; count < *num;++count) {
505 memcpy(np->sysname[count], cp, t+1); /* include null */
508 np->sysnamecount = *num;
511 /* Don't touch our arguments when called recursively */
512 *outname = np->sysname;
513 *num = np->sysnamecount;
515 return ENODEV; /* XXX */
521 /* 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 */
523 afs_nfsclient_GC(exporter, pag)
524 register struct afs_exporter *exporter;
525 register afs_int32 pag;
527 register struct nfsclientpag *np, **tnp, *nnp;
528 register afs_int32 i, delflag;
531 #if defined(AFS_SGIMP_ENV)
532 osi_Assert(ISAFS_GLOCK());
534 AFS_STATCNT(afs_nfsclient_GC);
535 MObtainWriteLock(&afs_xnfspag, 316);
536 for (i = 0; i < NNFSCLIENTS; i++) {
537 for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
540 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
542 if ((pag == -1) || (!pag && delflag)
543 || (pag && (np->refCount == 0) && (np->pag == pag))) {
545 for(count=0; count < np->sysnamecount;++count) {
546 afs_osi_Free(np->sysname[count], MAXSYSNAME);
548 afs_osi_Free(np, sizeof(struct nfsclientpag));
554 MReleaseWriteLock(&afs_xnfspag);
559 afs_nfsclient_stats(register struct afs_exporter *export)
561 /* Nothing much to do here yet since most important stats are collected
562 * directly in the afs_exporter structure itself */
563 AFS_STATCNT(afs_nfsclient_stats);
568 /* This is exposed so that vop_fid can test it, even if iauth is not
571 extern int afs_iauth_initd;
574 #ifdef AFS_AIX_IAUTH_ENV
575 char *afs_nfs_id = "AFSNFSTRANS";
576 /* afs_iauth_verify is the AFS authenticator for NFS.
581 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
582 struct AFS_UCRED *credp, struct exportinfo *exp)
585 struct nfsclientpag *nfs_pag;
587 struct afs_exporter *outexporter = 0;
590 /* Still needs basic test to see if exporter is on. And need to check the
591 * whole no submounts bit.
595 return 0; /* not us. */
597 /* Only care if it's AFS */
598 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
604 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
605 &dummypag, &outexporter);
606 if (!code && outexporter)
607 EXP_RELE(outexporter);
610 /* ensure anonymous cred. */
611 credp->cr_uid = credp->cr_ruid = (uid_t) - 2; /* anonymous */
614 /* Mark this thread as an NFS translator thread. */
615 credp->cr_rgid = NFSXLATOR_CRED;
621 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
622 * and -1 on failure. Can fail because DFS has already registered.
627 if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
635 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
638 afs_iauth_unregister()
641 nfs_iauth_unregister((unsigned long)afs_nfs_id);
644 #endif /* AFS_AIX_IAUTH_ENV */
651 #if defined(AFS_SGIMP_ENV)
652 osi_Assert(ISAFS_GLOCK());
654 AFS_STATCNT(afs_nfsclient_shutdown);
655 #ifdef AFS_AIX_IAUTH_ENV
656 afs_iauth_register();
658 afs_nfsclient_GC(afs_nfsexporter, -1);
659 init_nfsexporter = 0;
661 #endif /* AFS_NONFSTRANS */