openbsd-20021108
[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_cred->pc_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)  || defined(AFS_XBSD_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)  || defined(AFS_XBSD_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 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
313
314 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
315 int afs_getpag_val()
316 {
317   int pagvalue;
318   struct AFS_UCRED *credp = u.u_cred;
319   int gidset0, gidset1;
320
321   gidset0 = credp->cr_groups[0];
322   gidset1 = credp->cr_groups[1];
323   pagvalue=afs_get_pag_from_groups(gidset0, gidset1);
324   return pagvalue;
325 }
326 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
327
328
329 /* Note - needs to be available on AIX, others can be static - rework this */
330 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
331 int AddPag(struct proc *p, afs_int32 aval, struct AFS_UCRED **credpp)
332 #else
333 int AddPag(afs_int32 aval, struct AFS_UCRED **credpp)
334 #endif
335 {
336     afs_int32 newpag, code;
337     AFS_STATCNT(AddPag);
338 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
339     if ((code = setpag(p, credpp, aval, &newpag, 0)))
340 #else   /* AFS_OSF_ENV */
341     if ((code = setpag(credpp, aval, &newpag, 0)))
342 #endif
343 #if defined(KERNEL_HAVE_UERROR)
344         return (setuerror(code), code);
345 #else
346         return (code);
347 #endif
348     return 0;
349 }
350
351
352 int afs_InitReq(register struct vrequest *av, struct AFS_UCRED *acred)
353 {
354     AFS_STATCNT(afs_InitReq);
355     if (afs_shuttingdown) return EIO;
356     av->uid = PagInCred(acred);
357     if (av->uid == NOPAG) {
358         /* Afs doesn't use the unix uid for anuthing except a handle
359          * with which to find the actual authentication tokens so I
360          * think it's ok to use the real uid to make setuid
361          * programs (without setpag) to work properly.
362          */
363 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
364         av->uid = acred->cr_uid;    /* default when no pag is set */
365                                     /* bsd creds don't have ruid */
366 #else
367         av->uid = acred->cr_ruid;    /* default when no pag is set */
368 #endif
369     }
370     av->initd = 0;
371     return 0;
372 }
373
374
375
376 afs_uint32 afs_get_pag_from_groups(gid_t g0a, gid_t g1a)
377 {
378     afs_uint32 g0 = g0a;
379     afs_uint32 g1 = g1a;
380     afs_uint32 h, l, ret;
381
382     AFS_STATCNT(afs_get_pag_from_groups);
383     g0 -= 0x3f00;
384     g1 -= 0x3f00;
385     if (g0 < 0xc000 && g1 < 0xc000) {
386         l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
387         h = (g0 >> 14);
388         h = (g1 >> 14) + h + h + h;
389         ret = ((h << 28) | l);
390 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
391         return ret;
392 #else
393         /* Additional testing */
394         if (((ret >> 24) & 0xff) == 'A')
395             return ret;
396         else
397             return NOPAG;
398 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
399     }
400     return NOPAG;
401 }
402
403
404 void afs_get_groups_from_pag(afs_uint32 pag, gid_t *g0p, gid_t *g1p)
405 {
406     unsigned short g0, g1;
407
408
409     AFS_STATCNT(afs_get_groups_from_pag);
410 #if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
411     pag &= 0x7fffffff;
412 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
413     g0 = 0x3fff & (pag >> 14);
414     g1 = 0x3fff & pag;
415     g0 |= ((pag >> 28) / 3) << 14;
416     g1 |= ((pag >> 28) % 3) << 14;
417     *g0p = g0 + 0x3f00;
418     *g1p = g1 + 0x3f00;
419 }
420
421
422 afs_int32 PagInCred(const struct AFS_UCRED *cred)
423 {
424     afs_int32 pag;
425     gid_t g0, g1;
426
427     AFS_STATCNT(PagInCred);
428     if (cred == NULL) {
429         return NOPAG;
430     }
431 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
432     if (cred == NOCRED || cred == FSCRED) {
433         return NOPAG;
434     }
435     if (cred->cr_ngroups < 3) return NOPAG;
436     /* gid is stored in cr_groups[0] */
437     g0 = cred->cr_groups[1];
438     g1 = cred->cr_groups[2];
439 #else
440 #ifdef  AFS_AIX_ENV
441 #ifdef AFS_AIX51_ENV
442     if (kcred_getpag(cred, PAG_AFS, &pag) < 0 || pag == 0)
443         pag = NOPAG;
444     return pag;
445 #else
446     if (cred->cr_ngrps < 2) {
447         return NOPAG;
448     }
449 #endif
450 #else
451 #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DUX40_ENV) || defined(AFS_LINUX_ENV) || defined(AFS_XBSD_ENV)
452     if (cred->cr_ngroups < 2) return NOPAG;
453 #endif
454 #endif
455 #ifdef AFS_AIX51_ENV
456     g0 = cred->cr_groupset.gs_union.un_groups[0];
457     g1 = cred->cr_groupset.gs_union.un_groups[1];
458 #else
459     g0 = cred->cr_groups[0];
460     g1 = cred->cr_groups[1];
461 #endif
462 #endif
463     pag = (afs_int32)afs_get_pag_from_groups(g0, g1);
464     return pag;
465 }