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(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);
208 /* Do this early, so pag management knows */
209 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, 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 = 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(au)
307 struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
308 struct rx_securityClass *csec;
309 struct rx_connection *tconn;
310 SysNameList tsysnames;
315 int code, i, cellnum;
317 au->states |= UNFSGetCreds;
318 memset(&tcreds, 0, sizeof(tcreds));
319 memset(&tsysnames, 0, sizeof(tsysnames));
321 /* Get a connection */
322 /* This sucks a little. We should cache the connections or something.
323 * But at this point I don't yet think it's worth the effort.
325 csec = rxnull_NewClientSecurityObject();
327 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
330 /* Get the sysname, if needed */
331 if (!np->sysnamecount) {
333 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
336 tsysnames.SysNameList_len <= 0 ||
337 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
340 for(i = 0; i < np->sysnamecount; i++)
341 afs_osi_Free(np->sysname[i], MAXSYSNAME);
343 np->sysnamecount = tsysnames.SysNameList_len;
344 for(i = 0; i < np->sysnamecount; i++)
345 np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
346 afs_osi_Free(tsysnames.SysNameList_val,
347 tsysnames.SysNameList_len * sizeof(SysNameEnt));
350 /* Get credentials */
352 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
357 /* Now, set the credentials they gave us... */
358 for (i = 0; i < tcreds.CredInfos_len; i++) {
359 tcred = &tcreds.CredInfos_val[i];
361 /* Find the cell. If it is unknown to us, punt this entry. */
362 tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
363 afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
365 memset(tcred->ct.HandShakeKey, 0, 8);
366 memset(tcred->st.st_val, 0, tcred->st.st_len);
367 afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
370 cellnum = tcell->cellNum;
371 afs_PutCell(tcell, READ_LOCK);
373 /* Find the appropriate unixuser. This might be the same as
374 * the one we were passed (au), but that's OK.
376 tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
377 if (!(tu->exporter)) { /* Created new unixuser struct */
378 np->refCount++; /* so it won't disappear */
379 tu->exporter = (struct afs_exporter *)np;
382 /* free any old secret token, and keep the new one */
383 if (tu->stp != NULL) {
384 afs_osi_Free(tu->stp, tu->stLen);
386 tu->stp = tcred->st.st_val;
387 tu->stLen = tcred->st.st_len;
389 /* copy the clear token */
390 memset(&tu->ct, 0, sizeof(tu->ct));
391 memcpy(tu->ct.HandShakeKey, tcred->ct.HandShakeKey, 8);
392 memset(tcred->ct.HandShakeKey, 0, 8);
393 tu->ct.AuthHandle = tcred->ct.AuthHandle;
394 tu->ct.ViceId = tcred->ct.ViceId;
395 tu->ct.BeginTimestamp = tcred->ct.BeginTimestamp;
396 tu->ct.EndTimestamp = tcred->ct.EndTimestamp;
398 /* Set everything else, reset connections, and move on. */
399 tu->vid = tcred->vid;
400 tu->states |= UHasTokens;
401 tu->states &= ~UTokensBad;
402 afs_SetPrimary(tu, !!(tcred->states & UPrimary));
403 tu->tokenTime = osi_Time();
404 afs_ResetUserConns(tu);
405 afs_PutUser(tu, WRITE_LOCK);
407 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
411 rx_DestroyConnection(tconn);
413 au->states &= ~UNFSGetCreds;
414 afs_osi_Wakeup((void *)au);
418 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
420 afs_nfsclient_hold(np)
421 register struct nfsclientpag *np;
423 #if defined(AFS_SGIMP_ENV)
424 osi_Assert(ISAFS_GLOCK());
426 AFS_STATCNT(afs_nfsclient_hold);
431 /* check if this exporter corresponds to the specified host */
433 afs_nfsclient_checkhost(np, host)
434 register struct nfsclientpag *np;
436 if (np->type != EXP_NFS)
438 return np->host == host;
442 /* get the host for this exporter, or 0 if there is an error */
444 afs_nfsclient_gethost(np)
445 register struct nfsclientpag *np;
447 if (np->type != EXP_NFS)
453 /* 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 */
455 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname,
456 char ***outname, int *num, int allpags)
458 register struct nfsclientpag *tnp;
459 register afs_int32 i;
462 #if defined(AFS_SGIMP_ENV)
463 osi_Assert(ISAFS_GLOCK());
465 AFS_STATCNT(afs_nfsclient_sysname);
467 /* update every client, not just the one making the request */
469 MObtainWriteLock(&afs_xnfspag, 315);
470 for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
471 if (tnp != np && tnp->host == np->host)
472 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
474 MReleaseWriteLock(&afs_xnfspag);
477 for(count=0; count < np->sysnamecount;++count) {
478 afs_osi_Free(np->sysname[count], MAXSYSNAME);
479 np->sysname[count] = NULL;
481 for(count=0; count < *num;++count) {
482 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
485 for(count=0; count < *num;++count) {
487 memcpy(np->sysname[count], cp, t+1); /* include null */
490 np->sysnamecount = *num;
493 /* Don't touch our arguments when called recursively */
494 *outname = np->sysname;
495 *num = np->sysnamecount;
497 return ENODEV; /* XXX */
503 /* 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 */
505 afs_nfsclient_GC(exporter, pag)
506 register struct afs_exporter *exporter;
507 register afs_int32 pag;
509 register struct nfsclientpag *np, **tnp, *nnp;
510 register afs_int32 i, delflag;
513 #if defined(AFS_SGIMP_ENV)
514 osi_Assert(ISAFS_GLOCK());
516 AFS_STATCNT(afs_nfsclient_GC);
517 MObtainWriteLock(&afs_xnfspag, 316);
518 for (i = 0; i < NNFSCLIENTS; i++) {
519 for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
522 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
524 if ((pag == -1) || (!pag && delflag)
525 || (pag && (np->refCount == 0) && (np->pag == pag))) {
527 for(count=0; count < np->sysnamecount;++count) {
528 afs_osi_Free(np->sysname[count], MAXSYSNAME);
530 afs_osi_Free(np, sizeof(struct nfsclientpag));
536 MReleaseWriteLock(&afs_xnfspag);
541 afs_nfsclient_stats(register struct afs_exporter *export)
543 /* Nothing much to do here yet since most important stats are collected
544 * directly in the afs_exporter structure itself */
545 AFS_STATCNT(afs_nfsclient_stats);
550 /* This is exposed so that vop_fid can test it, even if iauth is not
553 extern int afs_iauth_initd;
556 #ifdef AFS_AIX_IAUTH_ENV
557 char *afs_nfs_id = "AFSNFSTRANS";
558 /* afs_iauth_verify is the AFS authenticator for NFS.
563 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
564 afs_ucred_t *credp, struct exportinfo *exp)
567 struct nfsclientpag *nfs_pag;
569 struct afs_exporter *outexporter = 0;
572 /* Still needs basic test to see if exporter is on. And need to check the
573 * whole no submounts bit.
577 return 0; /* not us. */
579 /* Only care if it's AFS */
580 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
586 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
587 &dummypag, &outexporter);
588 if (!code && outexporter)
589 EXP_RELE(outexporter);
592 /* ensure anonymous cred. */
593 set_cr_uid(credp, (uid_t) -2; /* anonymous */
594 set_cr_ruid(credp, (uid_t) -2;
597 /* Mark this thread as an NFS translator thread. */
598 set_cr_rgid(credp, NFSXLATOR_CRED);
604 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
605 * and -1 on failure. Can fail because DFS has already registered.
610 if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
618 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
621 afs_iauth_unregister()
624 nfs_iauth_unregister((unsigned long)afs_nfs_id);
627 #endif /* AFS_AIX_IAUTH_ENV */
634 #if defined(AFS_SGIMP_ENV)
635 osi_Assert(ISAFS_GLOCK());
637 AFS_STATCNT(afs_nfsclient_shutdown);
638 #ifdef AFS_AIX_IAUTH_ENV
639 afs_iauth_register();
641 afs_nfsclient_GC(afs_nfsexporter, -1);
642 init_nfsexporter = 0;
644 #endif /* AFS_NONFSTRANS */