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