handlecontext-20030623
[openafs.git] / src / afs / afs_osi_pag.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 /*
11  * Implements:
12  * genpag
13  * getpag
14  * afs_setpag
15  * AddPag
16  * afs_InitReq
17  * afs_get_pag_from_groups
18  * afs_get_groups_from_pag
19  * PagInCred
20  */
21
22 #include <afsconfig.h>
23 #include "afs/param.h"
24
25 RCSID("$Header$");
26
27 #include "afs/sysincludes.h"    /* Standard vendor system headers */
28 #include "afsincludes.h"        /* Afs-based standard headers */
29 #include "afs/afs_stats.h" /* statistics */
30 #include "afs/afs_cbqueue.h"
31 #include "afs/nfsclient.h"
32 #include "afs/afs_osidnlc.h"
33
34
35 /* Imported variables */
36 extern int afs_shuttingdown;
37
38 /* Exported variables */
39 afs_uint32 pag_epoch;
40 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
41 afs_uint32 pagCounter = 1;
42 #else
43 afs_uint32 pagCounter = 0;
44 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
45
46 /* Local variables */
47
48 /*
49  * Pags are implemented as follows: the set of groups whose long
50  * representation is '41XXXXXX' hex are used to represent the pags.
51  * Being a member of such a group means you are authenticated as pag
52  * XXXXXX (0x41 == 'A', for Andrew).  You are never authenticated as
53  * multiple pags at once.
54  *
55  * The function afs_InitReq takes a credential field and formats the
56  * corresponding venus request structure.  The uid field in the
57  * vrequest structure is set to the *pag* you are authenticated as, or
58  * the uid, if you aren't authenticated with a pag.
59  *
60  * The basic motivation behind pags is this: just because your unix
61  * uid is N doesn't mean that you should have the same privileges as
62  * anyone logged in on the machine as user N, since this would enable
63  * the superuser on the machine to sneak in and make use of anyone's
64  * authentication info, even that which is only accidentally left
65  * behind when someone leaves a public workstation.
66  *
67  * AFS doesn't use the unix uid for anything except
68  * a handle with which to find the actual authentication tokens
69  * anyway, so the pag is an alternative handle which is somewhat more
70  * secure (although of course not absolutely secure).
71 */
72 #if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
73 afs_uint32 genpag(void)
74 {
75     AFS_STATCNT(genpag);
76 #ifdef AFS_LINUX20_ENV
77     /* Ensure unique PAG's (mod 200 days) when reloading the client. */
78     return (('A' << 24) + ((pag_epoch + pagCounter++) & 0xffffff));
79 #else /* AFS_LINUX20_ENV */
80     return (('A' << 24) + (pagCounter++ & 0xffffff));
81 #endif /* AFS_LINUX20_ENV */
82 }
83
84 afs_uint32 getpag(void)
85 {
86     AFS_STATCNT(getpag);
87 #ifdef AFS_LINUX20_ENV
88     /* Ensure unique PAG's (mod 200 days) when reloading the client. */
89     return (('A' << 24) + ((pag_epoch + pagCounter) & 0xffffff));
90 #else
91     return (('A' << 24) + (pagCounter & 0xffffff));
92 #endif
93 }
94
95 #else
96
97 /* Web enhancement: we don't need to restrict pags to 41XXXXXX since
98  * we are not sharing the space with anyone.  So we use the full 32 bits. */
99
100 afs_uint32 genpag(void)
101 {
102     AFS_STATCNT(genpag);
103 #ifdef AFS_LINUX20_ENV
104     return (pag_epoch + pagCounter++);
105 #else
106     return (pagCounter++);
107 #endif /* AFS_LINUX20_ENV */
108 }
109
110 afs_uint32 getpag(void)
111 {
112     AFS_STATCNT(getpag);
113 #ifdef AFS_LINUX20_ENV
114     /* Ensure unique PAG's (mod 200 days) when reloading the client. */
115     return (pag_epoch + pagCounter);
116 #else
117     return (pagCounter);
118 #endif
119 }
120 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
121
122 /* used to require 10 seconds between each setpag to guarantee that
123  * PAGs never wrap - which would be a security hole.  If we presume
124  * that in ordinary operation, the average rate of PAG allocation
125  * will not exceed one per second, the 24 bits provided will be
126  * sufficient for ~200 days.  Unfortunately, if we merely assume that,
127  * there will be an opportunity for attack.  So we must enforce it.
128  * If we need to increase the average rate of PAG allocation, we
129  * should increase the number of bits in a PAG, and/or reduce our
130  * window in which we guarantee that the PAG counter won't wrap.
131  * By permitting an average of one new PAG per second, new PAGs can
132  * be allocated VERY frequently over a short period relative to total uptime.
133  * Of course, there's only an issue here if one user stays logged (and re-
134  * activates tokens repeatedly) for that entire period.
135  */
136
137 int
138 #if     defined(AFS_SUN5_ENV)
139 afs_setpag (struct AFS_UCRED **credpp)
140 #elif  defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
141 afs_setpag (struct proc *p, void *args, int *retval)
142 #else
143 afs_setpag (void) 
144 #endif
145 {
146     int code = 0;
147
148 #if defined(AFS_SGI53_ENV) && defined(MP)
149     /* This is our first chance to get the global lock. */
150     AFS_GLOCK();
151 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */    
152
153     AFS_STATCNT(afs_setpag);
154 #if     defined(AFS_SUN5_ENV)
155     if (!afs_suser(*credpp))
156 #elif  defined(AFS_OBSD_ENV)
157     if (!afs_osi_suser(p->p_ucred))
158 #else
159     if (!afs_suser())
160 #endif
161     {
162         while (osi_Time() - pag_epoch < pagCounter ) {
163             afs_osi_Wait(1000, (struct afs_osi_WaitHandle *) 0, 0);
164         }       
165     }
166
167 #if     defined(AFS_SUN5_ENV)
168     code = AddPag(genpag(), credpp);
169 #elif   defined(AFS_OSF_ENV) || defined(AFS_XBSD_ENV)
170     code = AddPag(p, genpag(), &p->p_rcred);
171 #elif   defined(AFS_AIX41_ENV)
172     {
173         struct ucred *credp;
174         struct ucred *credp0;
175         
176         credp = crref();
177         credp0 = credp;
178         code = AddPag(genpag(), &credp);
179         /* If AddPag() didn't make a new cred, then free our cred ref */
180         if (credp == credp0) {
181             crfree(credp);
182         }
183     }
184 #elif   defined(AFS_HPUX110_ENV)
185     {
186         struct ucred *credp = p_cred(u.u_procp);
187         code = AddPag(genpag(), &credp);
188     }
189 #elif   defined(AFS_SGI_ENV)
190     {
191         cred_t *credp;
192         credp = OSI_GET_CURRENT_CRED();
193         code = AddPag(genpag(), &credp);
194     }
195 #elif   defined(AFS_LINUX20_ENV)
196     {
197         struct AFS_UCRED *credp = crref();
198         code = AddPag(genpag(), &credp);
199         crfree(credp);
200     }
201 #elif defined(AFS_DARWIN_ENV)
202     {
203        struct ucred *credp = crdup(p->p_cred->pc_ucred);
204        code = AddPag(p, genpag(), &credp);
205        crfree(credp);
206     }
207 #else
208     code = AddPag(genpag(), &u.u_cred);
209 #endif
210
211     afs_Trace1(afs_iclSetp, CM_TRACE_SETPAG, ICL_TYPE_INT32, code);
212 #if defined(KERNEL_HAVE_UERROR)
213     if (!getuerror())
214         setuerror(code);
215 #endif
216 #if defined(AFS_SGI53_ENV) && defined(MP)
217     AFS_GUNLOCK();
218 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */
219     return (code);
220 }
221
222 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
223 /*
224  * afs_setpag_val
225  * This function is like setpag but sets the current thread's pag id to a
226  * caller-provided value instead of calling genpag().  This implements a
227  * form of token caching since the caller can recall a particular pag value
228  * for the thread to restore tokens, rather than reauthenticating.
229  */
230 int
231 #if     defined(AFS_SUN5_ENV)
232 afs_setpag_val (struct AFS_UCRED **credpp, int pagval)
233 #elif  defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
234 afs_setpag_val (struct proc *p, void *args, int *retval, int pagval)
235 #else
236 afs_setpag_val (int pagval) 
237 #endif
238 {
239     int code = 0;
240
241 #if defined(AFS_SGI53_ENV) && defined(MP)
242     /* This is our first chance to get the global lock. */
243     AFS_GLOCK();
244 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */    
245
246     AFS_STATCNT(afs_setpag);
247 #ifdef AFS_SUN5_ENV
248     if (!afs_suser(*credpp))
249 #else
250     if (!afs_suser())
251 #endif
252     {
253         while (osi_Time() - pag_epoch < pagCounter ) {
254             afs_osi_Wait(1000, (struct afs_osi_WaitHandle *) 0, 0);
255         }       
256     }
257
258 #if     defined(AFS_SUN5_ENV)
259     code = AddPag(pagval, credpp);
260 #elif   defined(AFS_OSF_ENV) || defined(AFS_XBSD_ENV)
261     code = AddPag(p, pagval, &p->p_rcred);
262 #elif   defined(AFS_AIX41_ENV)
263     {
264         struct ucred *credp;
265         struct ucred *credp0;
266         
267         credp = crref();
268         credp0 = credp;
269         code = AddPag(pagval, &credp);
270         /* If AddPag() didn't make a new cred, then free our cred ref */
271         if (credp == credp0) {
272             crfree(credp);
273         }
274     }
275 #elif   defined(AFS_HPUX110_ENV)
276     {
277         struct ucred *credp = p_cred(u.u_procp);
278         code = AddPag(pagval, &credp);
279     }
280 #elif   defined(AFS_SGI_ENV)
281     {
282         cred_t *credp;
283         credp = OSI_GET_CURRENT_CRED();
284         code = AddPag(pagval, &credp);
285     }
286 #elif   defined(AFS_LINUX20_ENV)
287     {
288         struct AFS_UCRED *credp = crref();
289         code = AddPag(pagval, &credp);
290         crfree(credp);
291     }
292 #elif defined(AFS_DARWIN_ENV)
293     {
294        struct ucred *credp = crdup(p->p_cred->pc_ucred);
295        code=AddPag(p, pagval, &credp);
296        crfree(credp);
297     }
298 #else
299     code = AddPag(pagval, &u.u_cred);
300 #endif
301
302     afs_Trace1(afs_iclSetp, CM_TRACE_SETPAG, ICL_TYPE_INT32, code);
303 #if defined(KERNEL_HAVE_UERROR)
304     if (!getuerror())
305         setuerror(code);
306 #endif
307 #if defined(AFS_SGI53_ENV) && defined(MP)
308     AFS_GUNLOCK();
309 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */    
310     return (code);
311 }
312
313 int afs_getpag_val()
314 {
315   int pagvalue;
316   struct AFS_UCRED *credp = u.u_cred;
317   int gidset0, gidset1;
318
319   gidset0 = credp->cr_groups[0];
320   gidset1 = credp->cr_groups[1];
321   pagvalue=afs_get_pag_from_groups(gidset0, gidset1);
322   return pagvalue;
323 }
324 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
325
326
327 /* Note - needs to be available on AIX, others can be static - rework this */
328 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
329 int AddPag(struct proc *p, afs_int32 aval, struct AFS_UCRED **credpp)
330 #else
331 int AddPag(afs_int32 aval, struct AFS_UCRED **credpp)
332 #endif
333 {
334     afs_int32 newpag, code;
335
336     AFS_STATCNT(AddPag);
337 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
338     if ((code = setpag(p, credpp, aval, &newpag, 0)))
339 #else
340     if ((code = setpag(credpp, aval, &newpag, 0)))
341 #endif
342 #if defined(KERNEL_HAVE_UERROR)
343         return (setuerror(code), code);
344 #else
345         return (code);
346 #endif
347     return 0;
348 }
349
350
351 int afs_InitReq(register struct vrequest *av, struct AFS_UCRED *acred)
352 {
353     AFS_STATCNT(afs_InitReq);
354     if (afs_shuttingdown) return EIO;
355     av->uid = PagInCred(acred);
356     if (av->uid == NOPAG) {
357         /* Afs doesn't use the unix uid for anuthing except a handle
358          * with which to find the actual authentication tokens so I
359          * think it's ok to use the real uid to make setuid
360          * programs (without setpag) to work properly.
361          */
362 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
363         if (acred == NOCRED)
364             av->uid = -2; /* XXX nobody... ?*/
365         else
366             av->uid = acred->cr_uid; /* bsd creds don't have ruid */
367 #else
368         av->uid = acred->cr_ruid;    /* default when no pag is set */
369 #endif
370     }
371     av->initd = 0;
372     return 0;
373 }
374
375
376
377 afs_uint32 afs_get_pag_from_groups(gid_t g0a, gid_t g1a)
378 {
379     afs_uint32 g0 = g0a;
380     afs_uint32 g1 = g1a;
381     afs_uint32 h, l, ret;
382
383     AFS_STATCNT(afs_get_pag_from_groups);
384     g0 -= 0x3f00;
385     g1 -= 0x3f00;
386     if (g0 < 0xc000 && g1 < 0xc000) {
387         l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
388         h = (g0 >> 14);
389         h = (g1 >> 14) + h + h + h;
390         ret = ((h << 28) | l);
391 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
392         return ret;
393 #else
394         /* Additional testing */
395         if (((ret >> 24) & 0xff) == 'A')
396             return ret;
397         else
398             return NOPAG;
399 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
400     }
401     return NOPAG;
402 }
403
404
405 void afs_get_groups_from_pag(afs_uint32 pag, gid_t *g0p, gid_t *g1p)
406 {
407     unsigned short g0, g1;
408
409
410     AFS_STATCNT(afs_get_groups_from_pag);
411 #if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
412     pag &= 0x7fffffff;
413 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
414     g0 = 0x3fff & (pag >> 14);
415     g1 = 0x3fff & pag;
416     g0 |= ((pag >> 28) / 3) << 14;
417     g1 |= ((pag >> 28) % 3) << 14;
418     *g0p = g0 + 0x3f00;
419     *g1p = g1 + 0x3f00;
420 }
421
422
423 afs_int32 PagInCred(const struct AFS_UCRED *cred)
424 {
425     afs_int32 pag;
426     gid_t g0, g1;
427
428     AFS_STATCNT(PagInCred);
429     if (cred == NULL) {
430         return NOPAG;
431     }
432 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
433     if (cred == NOCRED || cred == FSCRED) {
434         return NOPAG;
435     }
436     if (cred->cr_ngroups < 3) return NOPAG;
437     /* gid is stored in cr_groups[0] */
438     g0 = cred->cr_groups[1];
439     g1 = cred->cr_groups[2];
440 #else
441 #ifdef  AFS_AIX_ENV
442 #ifdef AFS_AIX51_ENV
443     if (kcred_getpag(cred, PAG_AFS, &pag) < 0 || pag == 0)
444         pag = NOPAG;
445     return pag;
446 #else
447     if (cred->cr_ngrps < 2) {
448         return NOPAG;
449     }
450 #endif
451 #else
452 #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DUX40_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_XBSD_ENV)
453     if (cred->cr_ngroups < 2) return NOPAG;
454 #endif
455 #endif
456 #ifdef AFS_AIX51_ENV
457     g0 = cred->cr_groupset.gs_union.un_groups[0];
458     g1 = cred->cr_groupset.gs_union.un_groups[1];
459 #else
460     g0 = cred->cr_groups[0];
461     g1 = cred->cr_groups[1];
462 #endif
463 #endif
464     pag = (afs_int32)afs_get_pag_from_groups(g0, g1);
465     return pag;
466 }