obsd-cred-ptr-20040120
[openafs.git] / src / afs / afs_nfsclnt.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 #include <afsconfig.h>
11 #include "afs/param.h"
12
13 RCSID
14     ("$Header$");
15
16 #if !defined(AFS_NONFSTRANS) || defined(AFS_AIX_IAUTH_ENV)
17 #ifndef AFS_DEC_ENV
18 #include "afs/sysincludes.h"    /* Standard vendor system headers */
19 #include "afsincludes.h"        /* Afs-based standard headers */
20 #include "afs/afs_stats.h"      /* statistics */
21 #include "afs/nfsclient.h"
22
23 int afs_nfsclient_reqhandler(), afs_nfsclient_hold(), afs_PutNfsClientPag();
24 int afs_nfsclient_sysname(), afs_nfsclient_GC(), afs_nfsclient_stats();
25 #ifdef AFS_AIX_IAUTH_ENV
26 int afs_allnfsreqs, afs_nfscalls;
27 #endif
28
29 /* routines exported to the "AFS exporter" layer */
30 struct exporterops nfs_exportops = {
31     afs_nfsclient_reqhandler,
32     afs_nfsclient_hold,
33     afs_PutNfsClientPag,        /* Used to be afs_nfsclient_rele */
34     afs_nfsclient_sysname,
35     afs_nfsclient_GC,
36     afs_nfsclient_stats,
37 };
38
39
40 struct nfsclientpag *afs_nfspags[NNFSCLIENTS];
41 afs_lock_t afs_xnfspag /*, afs_xnfsreq */ ;
42 extern struct afs_exporter *afs_nfsexporter;
43
44 /* Creates an nfsclientpag structure for the (uid, host) pair if one doesn't exist. RefCount is incremented and it's time stamped. */
45 static struct nfsclientpag *
46 afs_GetNfsClientPag(uid, host)
47      register afs_int32 uid, host;
48 {
49     register struct nfsclientpag *np;
50     register afs_int32 i, now;
51
52 #if defined(AFS_SGIMP_ENV)
53     osi_Assert(ISAFS_GLOCK());
54 #endif
55     AFS_STATCNT(afs_GetNfsClientPag);
56     i = NHash(host);
57     now = osi_Time();
58     MObtainWriteLock(&afs_xnfspag, 314);
59     for (np = afs_nfspags[i]; np; np = np->next) {
60         if (np->uid == uid && np->host == host) {
61             np->refCount++;
62             np->lastcall = now;
63             MReleaseWriteLock(&afs_xnfspag);
64             return np;
65         }
66     }
67     /* next try looking for NOPAG dude, if we didn't find an exact match */
68     for (np = afs_nfspags[i]; np; np = np->next) {
69         if (np->uid == NOPAG && np->host == host) {
70             np->refCount++;
71             np->lastcall = now;
72             MReleaseWriteLock(&afs_xnfspag);
73             return np;
74         }
75     }
76     np = (struct nfsclientpag *)afs_osi_Alloc(sizeof(struct nfsclientpag));
77     memset((char *)np, 0, sizeof(struct nfsclientpag));
78     /* Copy the necessary afs_exporter fields */
79     memcpy((char *)np, (char *)afs_nfsexporter, sizeof(struct afs_exporter));
80     np->next = afs_nfspags[i];
81     afs_nfspags[i] = np;
82     np->uid = uid;
83     np->host = host;
84     np->refCount = 1;
85     np->lastcall = now;
86     MReleaseWriteLock(&afs_xnfspag);
87     return np;
88 }
89
90
91 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
92 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
93 int
94 afs_PutNfsClientPag(np)
95      register struct nfsclientpag *np;
96 {
97 #if defined(AFS_SGIMP_ENV)
98     osi_Assert(ISAFS_GLOCK());
99 #endif
100     AFS_STATCNT(afs_PutNfsClientPag);
101     --np->refCount;
102 }
103
104
105 /* 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. */
106 static struct nfsclientpag *
107 afs_FindNfsClientPag(uid, host, pag)
108      register afs_int32 uid, host, pag;
109 {
110     register struct nfsclientpag *np;
111     register afs_int32 i;
112
113 #if defined(AFS_SGIMP_ENV)
114     osi_Assert(ISAFS_GLOCK());
115 #endif
116     AFS_STATCNT(afs_FindNfsClientPag);
117     i = NHash(host);
118     MObtainWriteLock(&afs_xnfspag, 315);
119     for (np = afs_nfspags[i]; np; np = np->next) {
120         if (np->host == host) {
121             if ((pag && pag == np->pag) || (!pag && (uid == np->uid))) {
122                 np->refCount++;
123                 np->lastcall = osi_Time();
124                 MReleaseWriteLock(&afs_xnfspag);
125                 return np;
126             }
127         }
128     }
129     /* still not there, try looking for a wildcard dude */
130     for (np = afs_nfspags[i]; np; np = np->next) {
131         if (np->host == host) {
132             if (np->uid == NOPAG) {
133                 np->refCount++;
134                 np->lastcall = osi_Time();
135                 MReleaseWriteLock(&afs_xnfspag);
136                 return np;
137             }
138         }
139     }
140     MReleaseWriteLock(&afs_xnfspag);
141     return NULL;
142 }
143
144
145 /* routine to initialize the exporter, made global so we can call it
146  * from pioctl calls.
147  */
148 struct afs_exporter *afs_nfsexported = 0;
149 static afs_int32 init_nfsexporter = 0;
150 afs_nfsclient_init()
151 {
152 #if defined(AFS_SGIMP_ENV)
153     osi_Assert(ISAFS_GLOCK());
154 #endif
155     if (!init_nfsexporter) {
156         extern struct afs_exporter *exporter_add();
157
158         init_nfsexporter = 1;
159         LOCK_INIT(&afs_xnfspag, "afs_xnfspag");
160         afs_nfsexported =
161             exporter_add(0, &nfs_exportops, EXP_EXPORTED, EXP_NFS, NULL);
162     }
163 }
164
165
166 /* Main handler routine for the NFS exporter. It's called in the early
167  * phases of any remote call (via the NFS server or pioctl).
168  */
169 int
170 afs_nfsclient_reqhandler(exporter, cred, host, pagparam, outexporter)
171      register struct afs_exporter *exporter, **outexporter;
172      struct AFS_UCRED **cred;
173      register afs_int32 host;
174      afs_int32 *pagparam;
175 {
176     register struct nfsclientpag *np, *tnp;
177     extern struct unixuser *afs_FindUser(), *afs_GetUser();
178     register struct unixuser *au = 0;
179     afs_int32 pag, code = 0;
180
181     AFS_ASSERT_GLOCK();
182     AFS_STATCNT(afs_nfsclient_reqhandler);
183     if (!afs_nfsexporter)
184         afs_nfsexporter = afs_nfsexported;
185
186     afs_nfsexporter->exp_stats.calls++;
187     if (!(afs_nfsexporter->exp_states & EXP_EXPORTED)) {
188         /* 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!)
189          */
190         afs_nfsexporter->exp_stats.rejectedcalls++;
191         return EINVAL;
192     }
193 /*    ObtainWriteLock(&afs_xnfsreq); */
194     pag = PagInCred(*cred);
195     if (pag != NOPAG) {
196         /* Do some minimal pag verification */
197         if (pag > getpag()) {
198             pag = NOPAG;        /* treat it as not paged since couldn't be good  */
199         } else {
200             if (au = afs_FindUser(pag, -1, READ_LOCK)) {
201                 if (!au->exporter) {
202                     pag = NOPAG;
203                     afs_PutUser(au, READ_LOCK);
204                     au = NULL;
205                 }
206             } else
207                 pag = NOPAG;    /*  No unixuser struct so pag not trusted  */
208         }
209     }
210     np = afs_FindNfsClientPag((*cred)->cr_uid, host, 0);
211     afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
212                ICL_TYPE_LONG, (*cred)->cr_uid, ICL_TYPE_INT32, host,
213                ICL_TYPE_POINTER, np);
214     if (!np) {
215         /* 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 */
216 #ifdef  AFS_OSF_ENV
217         if (code = setpag(u.u_procp, cred, -1, &pag, 1)) {      /* XXX u.u_procp is a no-op XXX */
218 #else
219         if (code = setpag(cred, -1, &pag, 1)) {
220 #endif
221             if (au)
222                 afs_PutUser(au, READ_LOCK);
223 /*          ReleaseWriteLock(&afs_xnfsreq);             */
224 #if defined(KERNEL_HAVE_UERROR)
225             setuerror(code);
226 #endif
227             return (code);
228         }
229         np = afs_GetNfsClientPag((*cred)->cr_uid, host);
230         np->pag = pag;
231     } else {
232         if (pag == NOPAG) {
233 #ifdef  AFS_OSF_ENV
234             if (code = setpag(u.u_procp, cred, np->pag, &pag, 1)) {     /* XXX u.u_procp is a no-op XXX */
235 #else
236             if (code = setpag(cred, np->pag, &pag, 1)) {
237 #endif
238                 afs_PutNfsClientPag(np);
239 /*              ReleaseWriteLock(&afs_xnfsreq); */
240 #if defined(KERNEL_HAVE_UERROR)
241                 setuerror(code);
242 #endif
243                 return (code);
244             }
245         } else if (au->exporter
246                    && ((struct afs_exporter *)np != au->exporter)) {
247             tnp = (struct nfsclientpag *)au->exporter;
248             if (tnp->uid && (tnp->uid != (afs_int32) - 2)) {    /* allow "root" initiators */
249                 /* Pag doesn't belong to caller; treat it as an unpaged call too */
250 #ifdef  AFS_OSF_ENV
251                 if (code = setpag(u.u_procp, cred, np->pag, &pag, 1)) { /* XXX u.u_procp is a no-op XXX */
252 #else
253                 if (code = setpag(cred, np->pag, &pag, 1)) {
254 #endif
255                     afs_PutNfsClientPag(np);
256                     afs_PutUser(au, READ_LOCK);
257                     /*      ReleaseWriteLock(&afs_xnfsreq);     */
258 #if defined(KERNEL_HAVE_UERROR)
259                     setuerror(code);
260 #endif
261                     return (code);
262                 }
263                 afs_nfsexporter->exp_stats.invalidpag++;
264             }
265         }
266     }
267     if (au)
268         afs_PutUser(au, READ_LOCK);
269     au = afs_GetUser(pag, -1, WRITE_LOCK);
270     if (!(au->exporter)) {      /* Created new unixuser struct */
271         np->refCount++;         /* so it won't disappear */
272         au->exporter = (struct afs_exporter *)np;
273     }
274     *pagparam = pag;
275     *outexporter = (struct afs_exporter *)np;
276     afs_PutUser(au, WRITE_LOCK);
277 #ifdef  AFS_OSF_ENV
278     (*cred)->cr_ruid = NFSXLATOR_CRED;  /* Identify it as nfs xlator call */
279 #else
280     (*cred)->cr_rgid = NFSXLATOR_CRED;  /* Identify it as nfs xlator call */
281 #endif
282 /*    ReleaseWriteLock(&afs_xnfsreq);   */
283     return 0;
284 }
285
286
287 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
288 int
289 afs_nfsclient_hold(np)
290      register struct nfsclientpag *np;
291 {
292 #if defined(AFS_SGIMP_ENV)
293     osi_Assert(ISAFS_GLOCK());
294 #endif
295     AFS_STATCNT(afs_nfsclient_hold);
296     np->refCount++;
297 }
298
299
300 /* 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 */
301 int 
302 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname, 
303                       char **outname[], int *num)
304 {
305     char *cp;
306     int count, t;
307 #if defined(AFS_SGIMP_ENV)
308     osi_Assert(ISAFS_GLOCK());
309 #endif
310     AFS_STATCNT(afs_nfsclient_sysname);
311     if (inname) {
312         if (np->sysname) {
313             for(count=0; count < np->sysnamecount;++count) {
314                 afs_osi_Free(np->sysname[count], MAXSYSNAME);
315             }
316         }
317         for(count=0; count < *num;++count) {
318             np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
319         }
320         cp = inname;
321         for(count=0; count < *num;++count) {
322             t = strlen(cp);
323             memcpy(np->sysname[count], cp, t+1); /* include null */
324             cp += t+1;
325         }
326         np->sysnamecount = *num;
327     } else if (!np->sysname) {
328         return ENODEV;      /* XXX */
329     }
330     *outname = np->sysname;
331     *num = np->sysnamecount;
332     return 0;
333 }
334
335
336 /* 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 */
337 int
338 afs_nfsclient_GC(exporter, pag)
339      register struct afs_exporter *exporter;
340      register afs_int32 pag;
341 {
342     register struct nfsclientpag *np, **tnp, *nnp;
343     register afs_int32 i, delflag;
344
345 #if defined(AFS_SGIMP_ENV)
346     osi_Assert(ISAFS_GLOCK());
347 #endif
348     AFS_STATCNT(afs_nfsclient_GC);
349     MObtainWriteLock(&afs_xnfspag, 316);
350     for (i = 0; i < NNFSCLIENTS; i++) {
351         for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
352             nnp = np->next;
353             delflag = 0;
354             if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
355                 delflag = 1;
356             if ((pag == -1) || (!pag && delflag)
357                 || (pag && (np->refCount == 0) && (np->pag == pag))) {
358                 *tnp = np->next;
359                 if (np->sysname)
360                     afs_osi_Free(np->sysname, MAXSYSNAME);
361                 afs_osi_Free(np, sizeof(struct nfsclientpag));
362             } else {
363                 tnp = &np->next;
364             }
365         }
366     }
367     MReleaseWriteLock(&afs_xnfspag);
368 }
369
370
371 int
372 afs_nfsclient_stats(export)
373      register struct afs_exporter *export;
374 {
375     /* Nothing much to do here yet since most important stats are collected directly in the afs_exporter structure itself */
376     AFS_STATCNT(afs_nfsclient_stats);
377     return 0;
378 }
379
380 #ifdef AFS_AIX41_ENV
381 /* This is exposed so that vop_fid can test it, even if iauth is not
382  *  installed.
383  */
384 extern int afs_iauth_initd;
385 #endif
386
387 #ifdef AFS_AIX_IAUTH_ENV
388 char *afs_nfs_id = "AFSNFSTRANS";
389 /* afs_iauth_verify is the AFS authenticator for NFS.
390  *
391  * always returns 0.
392  */
393 int
394 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
395                  struct AFS_UCRED *credp, struct exportinfo *exp)
396 {
397     int code;
398     struct nfsclientpag *nfs_pag;
399     afs_int32 dummypag;
400     struct afs_exporter *outexporter = 0;
401
402
403     /* Still needs basic test to see if exporter is on. And need to check the
404      * whole no submounts bit.
405      */
406
407     if (id != (long)id)
408         return 0;               /* not us. */
409
410     /* Only care if it's AFS */
411     if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
412         return 0;
413     }
414
415     AFS_GLOCK();
416     code =
417         afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
418                                  &dummypag, &outexporter);
419     if (!code && outexporter)
420         EXP_RELE(outexporter);
421
422     if (code) {
423         /* ensure anonymous cred. */
424         credp->cr_uid = credp->cr_ruid = (uid_t) - 2;   /* anonymous */
425     }
426
427     /* Mark this thread as an NFS translator thread. */
428     credp->cr_rgid = NFSXLATOR_CRED;
429
430     AFS_GUNLOCK();
431     return 0;
432 }
433
434 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
435  * and -1 on failure. Can fail because DFS has already registered.
436  */
437 int
438 afs_iauth_register()
439 {
440     if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
441         return -1;
442     else {
443         afs_iauth_initd = 1;
444         return 0;
445     }
446 }
447
448 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown. 
449  */
450 void
451 afs_iauth_unregister()
452 {
453     if (afs_iauth_initd)
454         nfs_iauth_unregister((unsigned long)afs_nfs_id);
455     afs_iauth_initd = 0;
456 }
457 #endif /* AFS_AIX_IAUTH_ENV */
458
459
460
461 shutdown_nfsclnt()
462 {
463 #if 0
464     extern int afs_allnfsreqs, afs_nfscalls;
465 #endif
466
467 #if defined(AFS_SGIMP_ENV)
468     osi_Assert(ISAFS_GLOCK());
469 #endif
470     AFS_STATCNT(afs_nfsclient_shutdown);
471 #ifdef AFS_AIX_IAUTH_ENV
472     afs_iauth_register();
473 #endif
474     afs_nfsclient_GC(afs_nfsexporter, -1);
475     init_nfsexporter = 0;
476 #if 0
477     /* The following are for the nfs/afs server */
478     afs_allnfsreqs = afs_nfscalls = 0;
479 #endif
480 }
481 #endif /* AFS_DEC_ENV */
482 #endif /* AFS_NONFSTRANS */