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(register afs_int32 uid, register afs_uint32 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 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 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_uint32 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 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 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 = 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 SysNameList tsysnames;
314 int code, i, cellnum;
316 au->states |= UNFSGetCreds;
317 memset(&tcreds, 0, sizeof(tcreds));
318 memset(&tsysnames, 0, sizeof(tsysnames));
320 /* Get a connection */
321 /* This sucks a little. We should cache the connections or something.
322 * But at this point I don't yet think it's worth the effort.
324 csec = rxnull_NewClientSecurityObject();
326 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
329 /* Get the sysname, if needed */
330 if (!np->sysnamecount) {
332 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
335 tsysnames.SysNameList_len <= 0 ||
336 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
339 for(i = 0; i < np->sysnamecount; i++)
340 afs_osi_Free(np->sysname[i], MAXSYSNAME);
342 np->sysnamecount = tsysnames.SysNameList_len;
343 for(i = 0; i < np->sysnamecount; i++)
344 np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
345 afs_osi_Free(tsysnames.SysNameList_val,
346 tsysnames.SysNameList_len * sizeof(SysNameEnt));
349 /* Get credentials */
351 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
356 /* Now, set the credentials they gave us... */
357 for (i = 0; i < tcreds.CredInfos_len; i++) {
358 tcred = &tcreds.CredInfos_val[i];
360 /* Find the cell. If it is unknown to us, punt this entry. */
361 tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
362 afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
364 memset(tcred->ct.HandShakeKey, 0, 8);
365 memset(tcred->st.st_val, 0, tcred->st.st_len);
366 afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
369 cellnum = tcell->cellNum;
370 afs_PutCell(tcell, READ_LOCK);
372 /* Find the appropriate unixuser. This might be the same as
373 * the one we were passed (au), but that's OK.
375 tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
376 if (!(tu->exporter)) { /* Created new unixuser struct */
377 np->refCount++; /* so it won't disappear */
378 tu->exporter = (struct afs_exporter *)np;
381 /* free any old secret token, and keep the new one */
382 if (tu->stp != NULL) {
383 afs_osi_Free(tu->stp, tu->stLen);
385 tu->stp = tcred->st.st_val;
386 tu->stLen = tcred->st.st_len;
388 /* copy the clear token */
389 memset(&tu->ct, 0, sizeof(tu->ct));
390 memcpy(tu->ct.HandShakeKey, tcred->ct.HandShakeKey, 8);
391 memset(tcred->ct.HandShakeKey, 0, 8);
392 tu->ct.AuthHandle = tcred->ct.AuthHandle;
393 tu->ct.ViceId = tcred->ct.ViceId;
394 tu->ct.BeginTimestamp = tcred->ct.BeginTimestamp;
395 tu->ct.EndTimestamp = tcred->ct.EndTimestamp;
397 /* Set everything else, reset connections, and move on. */
398 tu->vid = tcred->vid;
399 tu->states |= UHasTokens;
400 tu->states &= ~UTokensBad;
401 afs_SetPrimary(tu, !!(tcred->states & UPrimary));
402 tu->tokenTime = osi_Time();
403 afs_ResetUserConns(tu);
404 afs_PutUser(tu, WRITE_LOCK);
406 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
410 rx_DestroyConnection(tconn);
412 au->states &= ~UNFSGetCreds;
413 afs_osi_Wakeup((void *)au);
417 /* It's called whenever a new unixuser structure is created for the remote
418 * user associated with the nfsclientpag structure, np */
420 afs_nfsclient_hold(register struct nfsclientpag *np)
422 #if defined(AFS_SGIMP_ENV)
423 osi_Assert(ISAFS_GLOCK());
425 AFS_STATCNT(afs_nfsclient_hold);
430 /* check if this exporter corresponds to the specified host */
432 afs_nfsclient_checkhost(register struct nfsclientpag *np, afs_uint32 host)
434 if (np->type != EXP_NFS)
436 return np->host == host;
440 /* get the host for this exporter, or 0 if there is an error */
442 afs_nfsclient_gethost(register struct nfsclientpag *np)
444 if (np->type != EXP_NFS)
450 /* if inname is non-null, a new system name value is set for the remote
451 * user (inname contains the new sysname). In all cases, outname returns
452 * the current sysname value for this remote user */
454 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname,
455 char ***outname, int *num, int allpags)
457 register struct nfsclientpag *tnp;
458 register afs_int32 i;
461 #if defined(AFS_SGIMP_ENV)
462 osi_Assert(ISAFS_GLOCK());
464 AFS_STATCNT(afs_nfsclient_sysname);
466 /* update every client, not just the one making the request */
468 ObtainWriteLock(&afs_xnfspag, 315);
469 for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
470 if (tnp != np && tnp->host == np->host)
471 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
473 ReleaseWriteLock(&afs_xnfspag);
476 for(count=0; count < np->sysnamecount;++count) {
477 afs_osi_Free(np->sysname[count], MAXSYSNAME);
478 np->sysname[count] = NULL;
480 for(count=0; count < *num;++count) {
481 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
484 for(count=0; count < *num;++count) {
486 memcpy(np->sysname[count], cp, t+1); /* include null */
489 np->sysnamecount = *num;
492 /* Don't touch our arguments when called recursively */
493 *outname = np->sysname;
494 *num = np->sysnamecount;
496 return ENODEV; /* XXX */
502 /* Garbage collect routine for the nfs exporter. When pag is -1 then all
503 * entries are removed (used by the nfsclient_shutdown routine); else if
504 * it's non zero then only the entry with that pag is removed, else all
505 * "timedout" entries are removed. TimedOut entries are those who have no
506 * "unixuser" structures associated with them (i.e. unixusercnt == 0) and
507 * they haven't had any activity the last NFSCLIENTGC seconds */
509 afs_nfsclient_GC(register struct afs_exporter *exporter,
510 register afs_int32 pag)
512 register struct nfsclientpag *np, **tnp, *nnp;
513 register afs_int32 i, delflag;
516 #if defined(AFS_SGIMP_ENV)
517 osi_Assert(ISAFS_GLOCK());
519 AFS_STATCNT(afs_nfsclient_GC);
520 ObtainWriteLock(&afs_xnfspag, 316);
521 for (i = 0; i < NNFSCLIENTS; i++) {
522 for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
525 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
527 if ((pag == -1) || (!pag && delflag)
528 || (pag && (np->refCount == 0) && (np->pag == pag))) {
530 for(count=0; count < np->sysnamecount;++count) {
531 afs_osi_Free(np->sysname[count], MAXSYSNAME);
533 afs_osi_Free(np, sizeof(struct nfsclientpag));
539 ReleaseWriteLock(&afs_xnfspag);
544 afs_nfsclient_stats(register struct afs_exporter *export)
546 /* Nothing much to do here yet since most important stats are collected
547 * directly in the afs_exporter structure itself */
548 AFS_STATCNT(afs_nfsclient_stats);
553 /* This is exposed so that vop_fid can test it, even if iauth is not
556 extern int afs_iauth_initd;
559 #ifdef AFS_AIX_IAUTH_ENV
560 char *afs_nfs_id = "AFSNFSTRANS";
561 /* afs_iauth_verify is the AFS authenticator for NFS.
566 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
567 afs_ucred_t *credp, struct exportinfo *exp)
570 struct nfsclientpag *nfs_pag;
572 struct afs_exporter *outexporter = 0;
575 /* Still needs basic test to see if exporter is on. And need to check the
576 * whole no submounts bit.
580 return 0; /* not us. */
582 /* Only care if it's AFS */
583 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
589 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
590 &dummypag, &outexporter);
591 if (!code && outexporter)
592 EXP_RELE(outexporter);
595 /* ensure anonymous cred. */
596 afs_set_cr_uid(credp, (uid_t) -2; /* anonymous */
597 afs_set_cr_ruid(credp, (uid_t) -2;
600 /* Mark this thread as an NFS translator thread. */
601 afs_set_cr_rgid(credp, NFSXLATOR_CRED);
607 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
608 * and -1 on failure. Can fail because DFS has already registered.
611 afs_iauth_register(void)
613 if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
621 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
624 afs_iauth_unregister(void)
627 nfs_iauth_unregister((unsigned long)afs_nfs_id);
630 #endif /* AFS_AIX_IAUTH_ENV */
635 shutdown_nfsclnt(void)
637 #if defined(AFS_SGIMP_ENV)
638 osi_Assert(ISAFS_GLOCK());
640 AFS_STATCNT(afs_nfsclient_shutdown);
641 #ifdef AFS_AIX_IAUTH_ENV
642 afs_iauth_register();
644 afs_nfsclient_GC(afs_nfsexporter, -1);
645 init_nfsexporter = 0;
647 #endif /* AFS_NONFSTRANS */