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(afs_int32 uid, afs_uint32 host)
52 struct nfsclientpag *np;
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 = afs_osi_Alloc(sizeof(struct nfsclientpag));
80 osi_Assert(np != NULL);
81 memset(np, 0, sizeof(struct nfsclientpag));
82 /* Copy the necessary afs_exporter fields */
83 memcpy((char *)np, (char *)afs_nfsexporter, sizeof(struct afs_exporter));
84 np->next = afs_nfspags[i];
90 ReleaseWriteLock(&afs_xnfspag);
95 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
96 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
98 afs_PutNfsClientPag(np)
99 struct nfsclientpag *np;
101 #if defined(AFS_SGIMP_ENV)
102 osi_Assert(ISAFS_GLOCK());
104 AFS_STATCNT(afs_PutNfsClientPag);
109 /* Return the nfsclientpag structure associated with the (uid, host) or
110 * {pag, host} pair, if pag is nonzero. RefCount is incremented and it's
112 static struct nfsclientpag *
113 afs_FindNfsClientPag(afs_int32 uid, afs_uint32 host, afs_int32 pag)
115 struct nfsclientpag *np;
118 #if defined(AFS_SGIMP_ENV)
119 osi_Assert(ISAFS_GLOCK());
121 AFS_STATCNT(afs_FindNfsClientPag);
123 ObtainWriteLock(&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 ReleaseWriteLock(&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 ReleaseWriteLock(&afs_xnfspag);
145 ReleaseWriteLock(&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,
179 afs_uint32 host, afs_int32 *pagparam,
180 struct afs_exporter **outexporter)
182 struct nfsclientpag *np, *tnp;
183 extern struct unixuser *afs_FindUser(), *afs_GetUser();
184 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'.
195 * Set/Reset via a pioctl call (fs exportafs). Note that this is on
196 * top of the /etc/exports nfs requirement (i.e. /afs must be
197 * exported to all or whomever there too!)
199 afs_nfsexporter->exp_stats.rejectedcalls++;
202 /* ObtainWriteLock(&afs_xnfsreq); */
203 pag = PagInCred(*cred);
204 #if defined(AFS_SUN510_ENV)
205 uid = crgetuid(*cred);
207 uid = afs_cr_uid(*cred);
209 /* Do this early, so pag management knows */
210 afs_set_cr_rgid(*cred, NFSXLATOR_CRED); /* Identify it as nfs xlator call */
211 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
213 } else if (pag != NOPAG) {
214 /* Do some minimal pag verification */
215 if (pag > getpag()) {
216 pag = NOPAG; /* treat it as not paged since couldn't be good */
218 if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
221 afs_PutUser(au, READ_LOCK);
225 pag = NOPAG; /* No unixuser struct so pag not trusted */
228 np = afs_FindNfsClientPag(uid, host, 0);
229 afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
230 ICL_TYPE_LONG, afs_cr_uid(*cred), ICL_TYPE_INT32, host,
231 ICL_TYPE_POINTER, np);
232 /* If remote-pags are enabled, we are no longer interested in what PAG
233 * they claimed, and from here on we should behave as if they claimed
234 * none at all, which is to say we use the (local) pag named in the
235 * nfsclientpag structure (if any). This is deferred until here so
236 * that we can log the PAG they claimed.
238 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
241 /* Even if there is a "good" pag coming in we don't accept it if no
242 * nfsclientpag struct exists for the user since that would mean
243 * that the translator rebooted and therefore we ignore all older
246 if ((code = setpag(cred, -1, &pag, 0))) {
248 afs_PutUser(au, READ_LOCK);
249 /* ReleaseWriteLock(&afs_xnfsreq); */
250 #if defined(KERNEL_HAVE_UERROR)
255 np = afs_GetNfsClientPag(uid, host);
257 np->client_uid = afs_cr_uid(*cred);
260 if ((code = setpag(cred, np->pag, &pag, 0))) {
261 afs_PutNfsClientPag(np);
262 /* ReleaseWriteLock(&afs_xnfsreq); */
263 #if defined(KERNEL_HAVE_UERROR)
268 } else if (au->exporter
269 && ((struct afs_exporter *)np != au->exporter)) {
270 tnp = (struct nfsclientpag *)au->exporter;
271 if (tnp->uid && (tnp->uid != (afs_int32) - 2)) { /* allow "root" initiators */
272 /* Pag doesn't belong to caller; treat it as an unpaged call too */
273 if ((code = setpag(cred, np->pag, &pag, 0))) {
274 afs_PutNfsClientPag(np);
275 afs_PutUser(au, READ_LOCK);
276 /* ReleaseWriteLock(&afs_xnfsreq); */
277 #if defined(KERNEL_HAVE_UERROR)
282 afs_nfsexporter->exp_stats.invalidpag++;
287 afs_PutUser(au, READ_LOCK);
288 au = afs_GetUser(pag, -1, WRITE_LOCK);
289 if (!(au->exporter)) { /* Created new unixuser struct */
290 np->refCount++; /* so it won't disappear */
291 au->exporter = (struct afs_exporter *)np;
292 if ((afs_nfsexporter->exp_states & EXP_CALLBACK))
293 afs_nfsclient_getcreds(au);
294 } else while (au->states & UNFSGetCreds) {
295 afs_osi_Sleep((void *)au);
298 *outexporter = (struct afs_exporter *)np;
299 afs_PutUser(au, WRITE_LOCK);
300 /* ReleaseWriteLock(&afs_xnfsreq); */
305 afs_nfsclient_getcreds(struct unixuser *au)
307 struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
308 struct rx_securityClass *csec;
309 struct rx_connection *tconn;
310 union tokenUnion *tokenPtr;
311 struct rxkadToken *token;
312 SysNameList tsysnames;
317 int code, i, cellnum;
319 au->states |= UNFSGetCreds;
320 memset(&tcreds, 0, sizeof(tcreds));
321 memset(&tsysnames, 0, sizeof(tsysnames));
323 /* Get a connection */
324 /* This sucks a little. We should cache the connections or something.
325 * But at this point I don't yet think it's worth the effort.
327 csec = rxnull_NewClientSecurityObject();
329 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
332 /* Get the sysname, if needed */
333 if (!np->sysnamecount) {
335 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
338 tsysnames.SysNameList_len <= 0 ||
339 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
342 for(i = 0; i < np->sysnamecount; i++)
343 afs_osi_Free(np->sysname[i], MAXSYSNAME);
345 np->sysnamecount = tsysnames.SysNameList_len;
346 for(i = 0; i < np->sysnamecount; i++)
347 np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
348 afs_osi_Free(tsysnames.SysNameList_val,
349 tsysnames.SysNameList_len * sizeof(SysNameEnt));
352 /* Get credentials */
354 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
359 /* Now, set the credentials they gave us... */
360 for (i = 0; i < tcreds.CredInfos_len; i++) {
361 tcred = &tcreds.CredInfos_val[i];
363 /* Find the cell. If it is unknown to us, punt this entry. */
364 tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
365 afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
367 memset(tcred->ct.HandShakeKey, 0, 8);
368 memset(tcred->st.st_val, 0, tcred->st.st_len);
369 afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
372 cellnum = tcell->cellNum;
373 afs_PutCell(tcell, READ_LOCK);
375 /* Find the appropriate unixuser. This might be the same as
376 * the one we were passed (au), but that's OK.
378 tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
379 if (!(tu->exporter)) { /* Created new unixuser struct */
380 np->refCount++; /* so it won't disappear */
381 tu->exporter = (struct afs_exporter *)np;
384 afs_FreeTokens(&tu->tokens);
386 /* Add a new rxkad token. Using the afs_AddRxkadToken interface
387 * would require another copy, so we do this the hard way */
388 tokenPtr = afs_AddToken(&tu->tokens, 2);
389 token = &tokenPtr->rxkad;
390 token->ticket = tcred->st.st_val;
391 token->ticketLen = tcred->st.st_len;
393 /* copy the clear token */
394 memset(&token->clearToken, 0, sizeof(token->clearToken));
395 memcpy(token->clearToken.HandShakeKey, tcred->ct.HandShakeKey, 8);
396 memset(tcred->ct.HandShakeKey, 0, 8);
397 token->clearToken.AuthHandle = tcred->ct.AuthHandle;
398 token->clearToken.ViceId = tcred->ct.ViceId;
399 token->clearToken.BeginTimestamp = tcred->ct.BeginTimestamp;
400 token->clearToken.EndTimestamp = tcred->ct.EndTimestamp;
402 /* Set everything else, reset connections, and move on. */
403 tu->viceId = tcred->vid;
404 tu->states |= UHasTokens;
405 tu->states &= ~UTokensBad;
406 afs_SetPrimary(tu, !!(tcred->states & UPrimary));
407 tu->tokenTime = osi_Time();
408 afs_ResetUserConns(tu);
409 afs_PutUser(tu, WRITE_LOCK);
411 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
415 rx_DestroyConnection(tconn);
417 au->states &= ~UNFSGetCreds;
418 afs_osi_Wakeup((void *)au);
422 /* It's called whenever a new unixuser structure is created for the remote
423 * user associated with the nfsclientpag structure, np */
425 afs_nfsclient_hold(struct nfsclientpag *np)
427 #if defined(AFS_SGIMP_ENV)
428 osi_Assert(ISAFS_GLOCK());
430 AFS_STATCNT(afs_nfsclient_hold);
435 /* check if this exporter corresponds to the specified host */
437 afs_nfsclient_checkhost(struct nfsclientpag *np, afs_uint32 host)
439 if (np->type != EXP_NFS)
441 return np->host == host;
445 /* get the host for this exporter, or 0 if there is an error */
447 afs_nfsclient_gethost(struct nfsclientpag *np)
449 if (np->type != EXP_NFS)
455 /* if inname is non-null, a new system name value is set for the remote
456 * user (inname contains the new sysname). In all cases, outname returns
457 * the current sysname value for this remote user */
459 afs_nfsclient_sysname(struct nfsclientpag *np, char *inname,
460 char ***outname, int *num, int allpags)
462 struct nfsclientpag *tnp;
466 #if defined(AFS_SGIMP_ENV)
467 osi_Assert(ISAFS_GLOCK());
469 AFS_STATCNT(afs_nfsclient_sysname);
471 /* update every client, not just the one making the request */
473 ObtainWriteLock(&afs_xnfspag, 315);
474 for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
475 if (tnp != np && tnp->host == np->host)
476 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
478 ReleaseWriteLock(&afs_xnfspag);
481 for(count=0; count < np->sysnamecount;++count) {
482 afs_osi_Free(np->sysname[count], MAXSYSNAME);
483 np->sysname[count] = NULL;
485 for(count=0; count < *num;++count) {
486 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
487 osi_Assert(np->sysname[count] != NULL);
490 for(count=0; count < *num;++count) {
492 memcpy(np->sysname[count], cp, t+1); /* include null */
495 np->sysnamecount = *num;
498 /* Don't touch our arguments when called recursively */
499 *outname = np->sysname;
500 *num = np->sysnamecount;
502 return ENODEV; /* XXX */
508 /* Garbage collect routine for the nfs exporter. When pag is -1 then all
509 * entries are removed (used by the nfsclient_shutdown routine); else if
510 * it's non zero then only the entry with that pag is removed, else all
511 * "timedout" entries are removed. TimedOut entries are those who have no
512 * "unixuser" structures associated with them (i.e. unixusercnt == 0) and
513 * they haven't had any activity the last NFSCLIENTGC seconds */
515 afs_nfsclient_GC(struct afs_exporter *exporter,
518 struct nfsclientpag *np, **tnp, *nnp;
519 afs_int32 i, delflag;
522 #if defined(AFS_SGIMP_ENV)
523 osi_Assert(ISAFS_GLOCK());
525 AFS_STATCNT(afs_nfsclient_GC);
526 ObtainWriteLock(&afs_xnfspag, 316);
527 for (i = 0; i < NNFSCLIENTS; i++) {
528 for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
531 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
533 if ((pag == -1) || (!pag && delflag)
534 || (pag && (np->refCount == 0) && (np->pag == pag))) {
536 for(count=0; count < np->sysnamecount;++count) {
537 afs_osi_Free(np->sysname[count], MAXSYSNAME);
539 afs_osi_Free(np, sizeof(struct nfsclientpag));
545 ReleaseWriteLock(&afs_xnfspag);
550 afs_nfsclient_stats(struct afs_exporter *export)
552 /* Nothing much to do here yet since most important stats are collected
553 * directly in the afs_exporter structure itself */
554 AFS_STATCNT(afs_nfsclient_stats);
559 /* This is exposed so that vop_fid can test it, even if iauth is not
562 extern int afs_iauth_initd;
565 #ifdef AFS_AIX_IAUTH_ENV
566 char *afs_nfs_id = "AFSNFSTRANS";
567 /* afs_iauth_verify is the AFS authenticator for NFS.
572 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
573 afs_ucred_t *credp, struct exportinfo *exp)
576 struct nfsclientpag *nfs_pag;
578 struct afs_exporter *outexporter = 0;
581 /* Still needs basic test to see if exporter is on. And need to check the
582 * whole no submounts bit.
586 return 0; /* not us. */
588 /* Only care if it's AFS */
589 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
595 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
596 &dummypag, &outexporter);
597 if (!code && outexporter)
598 EXP_RELE(outexporter);
601 /* ensure anonymous cred. */
602 afs_set_cr_uid(credp, (uid_t) -2; /* anonymous */
603 afs_set_cr_ruid(credp, (uid_t) -2;
606 /* Mark this thread as an NFS translator thread. */
607 afs_set_cr_rgid(credp, 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.
617 afs_iauth_register(void)
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(void)
633 nfs_iauth_unregister((unsigned long)afs_nfs_id);
636 #endif /* AFS_AIX_IAUTH_ENV */
641 shutdown_nfsclnt(void)
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 */