DEVEL15-keyring-pag-avoid-shadowing-20070208
[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 #ifdef AFS_LINUX26_ONEGROUP_ENV
48 #define NUMPAGGROUPS 1
49 #else
50 #define NUMPAGGROUPS 2
51 #endif
52 /* Local variables */
53
54 /*
55  * Pags are implemented as follows: the set of groups whose long
56  * representation is '41XXXXXX' hex are used to represent the pags.
57  * Being a member of such a group means you are authenticated as pag
58  * XXXXXX (0x41 == 'A', for Andrew).  You are never authenticated as
59  * multiple pags at once.
60  *
61  * The function afs_InitReq takes a credential field and formats the
62  * corresponding venus request structure.  The uid field in the
63  * vrequest structure is set to the *pag* you are authenticated as, or
64  * the uid, if you aren't authenticated with a pag.
65  *
66  * The basic motivation behind pags is this: just because your unix
67  * uid is N doesn't mean that you should have the same privileges as
68  * anyone logged in on the machine as user N, since this would enable
69  * the superuser on the machine to sneak in and make use of anyone's
70  * authentication info, even that which is only accidentally left
71  * behind when someone leaves a public workstation.
72  *
73  * AFS doesn't use the unix uid for anything except
74  * a handle with which to find the actual authentication tokens
75  * anyway, so the pag is an alternative handle which is somewhat more
76  * secure (although of course not absolutely secure).
77 */
78 #if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
79 afs_uint32
80 genpag(void)
81 {
82     AFS_STATCNT(genpag);
83 #ifdef AFS_LINUX20_ENV
84     /* Ensure unique PAG's (mod 200 days) when reloading the client. */
85     return (('A' << 24) + ((pag_epoch + pagCounter++) & 0xffffff));
86 #else /* AFS_LINUX20_ENV */
87     return (('A' << 24) + (pagCounter++ & 0xffffff));
88 #endif /* AFS_LINUX20_ENV */
89 }
90
91 afs_uint32
92 getpag(void)
93 {
94     AFS_STATCNT(getpag);
95 #ifdef AFS_LINUX20_ENV
96     /* Ensure unique PAG's (mod 200 days) when reloading the client. */
97     return (('A' << 24) + ((pag_epoch + pagCounter) & 0xffffff));
98 #else
99     return (('A' << 24) + (pagCounter & 0xffffff));
100 #endif
101 }
102
103 #else
104
105 /* Web enhancement: we don't need to restrict pags to 41XXXXXX since
106  * we are not sharing the space with anyone.  So we use the full 32 bits. */
107
108 afs_uint32
109 genpag(void)
110 {
111     AFS_STATCNT(genpag);
112 #ifdef AFS_LINUX20_ENV
113     return (pag_epoch + pagCounter++);
114 #else
115     return (pagCounter++);
116 #endif /* AFS_LINUX20_ENV */
117 }
118
119 afs_uint32
120 getpag(void)
121 {
122     AFS_STATCNT(getpag);
123 #ifdef AFS_LINUX20_ENV
124     /* Ensure unique PAG's (mod 200 days) when reloading the client. */
125     return (pag_epoch + pagCounter);
126 #else
127     return (pagCounter);
128 #endif
129 }
130 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
131
132 /* used to require 10 seconds between each setpag to guarantee that
133  * PAGs never wrap - which would be a security hole.  If we presume
134  * that in ordinary operation, the average rate of PAG allocation
135  * will not exceed one per second, the 24 bits provided will be
136  * sufficient for ~200 days.  Unfortunately, if we merely assume that,
137  * there will be an opportunity for attack.  So we must enforce it.
138  * If we need to increase the average rate of PAG allocation, we
139  * should increase the number of bits in a PAG, and/or reduce our
140  * window in which we guarantee that the PAG counter won't wrap.
141  * By permitting an average of one new PAG per second, new PAGs can
142  * be allocated VERY frequently over a short period relative to total uptime.
143  * Of course, there's only an issue here if one user stays logged (and re-
144  * activates tokens repeatedly) for that entire period.
145  */
146
147 static int afs_pag_sleepcnt = 0;
148
149 static int
150 afs_pag_sleep(struct AFS_UCRED **acred)
151 {
152     int rv = 0;
153
154     if (!afs_suser(acred)) {
155         if(osi_Time() - pag_epoch < pagCounter) {
156             rv = 1;
157         }
158     }
159
160     return rv;
161 }
162
163 static int
164 afs_pag_wait(struct AFS_UCRED **acred)
165 {
166     if (afs_pag_sleep(acred)) {
167         if (!afs_pag_sleepcnt) {
168             printf("%s() PAG throttling triggered, pid %d... sleeping.  sleepcnt %d\n",
169                    "afs_pag_wait", osi_getpid(), afs_pag_sleepcnt);
170         }
171
172         afs_pag_sleepcnt++;
173
174         do {
175             /* XXX spins on EINTR */
176             afs_osi_Wait(1000, (struct afs_osi_WaitHandle *)0, 0);
177         } while (afs_pag_sleep(acred));
178
179         afs_pag_sleepcnt--;
180     }
181
182     return 0;
183 }
184
185 int
186 #if     defined(AFS_SUN5_ENV)
187 afs_setpag(struct AFS_UCRED **credpp)
188 #elif  defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
189 afs_setpag(struct proc *p, void *args, int *retval)
190 #else
191 afs_setpag(void)
192 #endif
193 {
194
195 #if     defined(AFS_SUN5_ENV)
196     struct AFS_UCRED **acred = *credpp;
197 #elif  defined(AFS_OBSD_ENV)
198     struct AFS_UCRED **acred = &p->p_ucred;
199 #else
200     struct AFS_UCRED **acred = NULL;
201 #endif
202
203     int code = 0;
204
205 #if defined(AFS_SGI53_ENV) && defined(MP)
206     /* This is our first chance to get the global lock. */
207     AFS_GLOCK();
208 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */
209
210     AFS_STATCNT(afs_setpag);
211
212     afs_pag_wait(acred);
213
214
215 #if     defined(AFS_SUN5_ENV)
216     code = AddPag(genpag(), credpp);
217 #elif   defined(AFS_OSF_ENV) || defined(AFS_XBSD_ENV)
218     code = AddPag(p, genpag(), &p->p_rcred);
219 #elif   defined(AFS_AIX41_ENV)
220     {
221         struct ucred *credp;
222         struct ucred *credp0;
223
224         credp = crref();
225         credp0 = credp;
226         code = AddPag(genpag(), &credp);
227         /* If AddPag() didn't make a new cred, then free our cred ref */
228         if (credp == credp0) {
229             crfree(credp);
230         }
231     }
232 #elif   defined(AFS_HPUX110_ENV)
233     {
234         struct ucred *credp = p_cred(u.u_procp);
235         code = AddPag(genpag(), &credp);
236     }
237 #elif   defined(AFS_SGI_ENV)
238     {
239         cred_t *credp;
240         credp = OSI_GET_CURRENT_CRED();
241         code = AddPag(genpag(), &credp);
242     }
243 #elif   defined(AFS_LINUX20_ENV)
244     {
245         struct AFS_UCRED *credp = crref();
246         code = AddPag(genpag(), &credp);
247         crfree(credp);
248     }
249 #elif defined(AFS_DARWIN80_ENV)
250     {
251         struct ucred *credp = kauth_cred_proc_ref(p);
252         code = AddPag(p, genpag(), &credp);
253         kauth_cred_rele(credp);
254     }
255 #elif defined(AFS_DARWIN_ENV)
256     {
257         struct ucred *credp = crdup(p->p_cred->pc_ucred);
258         code = AddPag(p, genpag(), &credp);
259         crfree(credp);
260     }
261 #else
262     code = AddPag(genpag(), &u.u_cred);
263 #endif
264
265     afs_Trace1(afs_iclSetp, CM_TRACE_SETPAG, ICL_TYPE_INT32, code);
266
267 #if defined(KERNEL_HAVE_UERROR)
268     if (!getuerror())
269         setuerror(code);
270 #endif
271
272 #if defined(AFS_SGI53_ENV) && defined(MP)
273     AFS_GUNLOCK();
274 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */
275
276     return (code);
277 }
278
279 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
280 /*
281  * afs_setpag_val
282  * This function is like setpag but sets the current thread's pag id to a
283  * caller-provided value instead of calling genpag().  This implements a
284  * form of token caching since the caller can recall a particular pag value
285  * for the thread to restore tokens, rather than reauthenticating.
286  */
287 int
288 #if     defined(AFS_SUN5_ENV)
289 afs_setpag_val(struct AFS_UCRED **credpp, int pagval)
290 #elif  defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
291 afs_setpag_val(struct proc *p, void *args, int *retval, int pagval)
292 #else
293 afs_setpag_val(int pagval)
294 #endif
295 {
296
297 #if     defined(AFS_SUN5_ENV)
298     struct AFS_UCRED **acred = *credp;
299 #elif  defined(AFS_OBSD_ENV)
300     struct AFS_UCRED **acred = &p->p_ucred;
301 #else
302     struct AFS_UCRED **acred = NULL;
303 #endif
304
305     int code = 0;
306
307 #if defined(AFS_SGI53_ENV) && defined(MP)
308     /* This is our first chance to get the global lock. */
309     AFS_GLOCK();
310 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */
311
312     AFS_STATCNT(afs_setpag);
313
314     afs_pag_wait(acred);
315
316 #if     defined(AFS_SUN5_ENV)
317     code = AddPag(pagval, credpp);
318 #elif   defined(AFS_OSF_ENV) || defined(AFS_XBSD_ENV)
319     code = AddPag(p, pagval, &p->p_rcred);
320 #elif   defined(AFS_AIX41_ENV)
321     {
322         struct ucred *credp;
323         struct ucred *credp0;
324
325         credp = crref();
326         credp0 = credp;
327         code = AddPag(pagval, &credp);
328         /* If AddPag() didn't make a new cred, then free our cred ref */
329         if (credp == credp0) {
330             crfree(credp);
331         }
332     }
333 #elif   defined(AFS_HPUX110_ENV)
334     {
335         struct ucred *credp = p_cred(u.u_procp);
336         code = AddPag(pagval, &credp);
337     }
338 #elif   defined(AFS_SGI_ENV)
339     {
340         cred_t *credp;
341         credp = OSI_GET_CURRENT_CRED();
342         code = AddPag(pagval, &credp);
343     }
344 #elif   defined(AFS_LINUX20_ENV)
345     {
346         struct AFS_UCRED *credp = crref();
347         code = AddPag(pagval, &credp);
348         crfree(credp);
349     }
350 #elif defined(AFS_DARWIN_ENV)
351     {
352         struct ucred *credp = crdup(p->p_cred->pc_ucred);
353         code = AddPag(p, pagval, &credp);
354         crfree(credp);
355     }
356 #else
357     code = AddPag(pagval, &u.u_cred);
358 #endif
359
360     afs_Trace1(afs_iclSetp, CM_TRACE_SETPAG, ICL_TYPE_INT32, code);
361 #if defined(KERNEL_HAVE_UERROR)
362     if (!getuerror())
363         setuerror(code);
364 #endif
365 #if defined(AFS_SGI53_ENV) && defined(MP)
366     AFS_GUNLOCK();
367 #endif /* defined(AFS_SGI53_ENV) && defined(MP) */
368     return (code);
369 }
370
371 #ifndef AFS_LINUX26_ONEGROUP_ENV
372 int
373 afs_getpag_val()
374 {
375     int pagvalue;
376     struct AFS_UCRED *credp = u.u_cred;
377     gid_t gidset0, gidset1;
378 #ifdef AFS_SUN510_ENV
379     const gid_t *gids;
380
381     gids = crgetgroups(*credp);
382     gidset0 = gids[0];
383     gidset1 = gids[1];
384 #else
385     gidset0 = credp->cr_groups[0];
386     gidset1 = credp->cr_groups[1];
387 #endif
388     pagvalue = afs_get_pag_from_groups(gidset0, gidset1);
389     return pagvalue;
390 }
391 #endif
392 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
393
394
395 /* Note - needs to be available on AIX, others can be static - rework this */
396 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
397 int
398 AddPag(struct proc *p, afs_int32 aval, struct AFS_UCRED **credpp)
399 #else
400 int
401 AddPag(afs_int32 aval, struct AFS_UCRED **credpp)
402 #endif
403 {
404     afs_int32 newpag, code;
405
406     AFS_STATCNT(AddPag);
407 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
408     if ((code = setpag(p, credpp, aval, &newpag, 0)))
409 #else
410     if ((code = setpag(credpp, aval, &newpag, 0)))
411 #endif
412 #if defined(KERNEL_HAVE_UERROR)
413         return (setuerror(code), code);
414 #else
415         return (code);
416 #endif
417     return 0;
418 }
419
420
421 int
422 afs_InitReq(register struct vrequest *av, struct AFS_UCRED *acred)
423 {
424     int code;
425
426     AFS_STATCNT(afs_InitReq);
427     memset(av, 0, sizeof(*av));
428     if (afs_shuttingdown)
429         return EIO;
430
431 #ifdef AFS_LINUX26_ENV
432 #if !defined(AFS_NONFSTRANS)
433     if (osi_linux_nfs_initreq(av, acred, &code))
434         return code;
435 #endif
436 #endif
437
438     av->uid = PagInCred(acred);
439     if (av->uid == NOPAG) {
440         /* Afs doesn't use the unix uid for anuthing except a handle
441          * with which to find the actual authentication tokens so I
442          * think it's ok to use the real uid to make setuid
443          * programs (without setpag) to work properly.
444          */
445 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
446         if (acred == NOCRED)
447             av->uid = -2;       /* XXX nobody... ? */
448         else
449             av->uid = acred->cr_uid;    /* bsd creds don't have ruid */
450 #elif defined(AFS_SUN510_ENV)
451         av->uid = crgetruid(acred);
452 #else
453         av->uid = acred->cr_ruid;       /* default when no pag is set */
454 #endif
455     }
456     av->initd = 0;
457     return 0;
458 }
459
460
461 #ifdef AFS_LINUX26_ONEGROUP_ENV
462 afs_uint32
463 afs_get_pag_from_groups(struct group_info *group_info)
464 {
465     afs_uint32 g0 = 0;
466     afs_uint32 i;
467
468     AFS_STATCNT(afs_get_pag_from_groups);
469     for (i = 0; (i < group_info->ngroups && 
470                  (g0 = GROUP_AT(group_info, i)) != (gid_t) NOGROUP); i++) {
471         if (((g0 >> 24) & 0xff) == 'A')
472             return g0;
473     }
474     return NOPAG;
475 }
476 #else
477 afs_uint32
478 afs_get_pag_from_groups(gid_t g0a, gid_t g1a)
479 {
480     afs_uint32 g0 = g0a;
481     afs_uint32 g1 = g1a;
482     afs_uint32 h, l, ret;
483
484     AFS_STATCNT(afs_get_pag_from_groups);
485
486     g0 -= 0x3f00;
487     g1 -= 0x3f00;
488     if (g0 < 0xc000 && g1 < 0xc000) {
489         l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
490         h = (g0 >> 14);
491         h = (g1 >> 14) + h + h + h;
492         ret = ((h << 28) | l);
493 #if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
494         return ret;
495 #else
496         /* Additional testing */
497         if (((ret >> 24) & 0xff) == 'A')
498             return ret;
499 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
500     }
501     return NOPAG;
502 }
503 #endif
504
505 void
506 afs_get_groups_from_pag(afs_uint32 pag, gid_t * g0p, gid_t * g1p)
507 {
508     unsigned short g0, g1;
509
510
511     AFS_STATCNT(afs_get_groups_from_pag);
512 #ifdef AFS_LINUX26_ONEGROUP_ENV
513     *g0p = pag;
514     *g1p = 0;
515 #else
516 #if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
517     pag &= 0x7fffffff;
518 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
519     g0 = 0x3fff & (pag >> 14);
520     g1 = 0x3fff & pag;
521     g0 |= ((pag >> 28) / 3) << 14;
522     g1 |= ((pag >> 28) % 3) << 14;
523     *g0p = g0 + 0x3f00;
524     *g1p = g1 + 0x3f00;
525 #endif
526 }
527
528
529 afs_int32
530 PagInCred(const struct AFS_UCRED *cred)
531 {
532     afs_int32 pag;
533     gid_t g0, g1;
534 #if defined(AFS_SUN510_ENV)
535     const gid_t *gids;
536     int ngroups;
537 #endif
538
539     AFS_STATCNT(PagInCred);
540     if (cred == NULL || cred == afs_osi_credp) {
541         return NOPAG;
542     }
543 #if defined(AFS_SUN510_ENV)
544     gids = crgetgroups(cred);
545     ngroups = crgetngroups(cred);
546 #endif
547 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
548     if (cred == NOCRED || cred == FSCRED) {
549         return NOPAG;
550     }
551     if (cred->cr_ngroups < 3)
552         return NOPAG;
553     /* gid is stored in cr_groups[0] */
554     g0 = cred->cr_groups[1];
555     g1 = cred->cr_groups[2];
556 #else
557 #if defined(AFS_AIX51_ENV)
558     if (kcred_getpag(cred, PAG_AFS, &pag) < 0 || pag == 0)
559         pag = NOPAG;
560     return pag;
561 #elif defined(AFS_AIX_ENV)
562     if (cred->cr_ngrps < 2) {
563         return NOPAG;
564     }
565 #elif defined(AFS_LINUX26_ENV)
566     if (cred->cr_group_info->ngroups < NUMPAGGROUPS) {
567         pag = NOPAG;
568         goto out;
569     }
570 #elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DUX40_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_XBSD_ENV)
571 #if defined(AFS_SUN510_ENV)
572     if (ngroups < 2) {
573 #else
574     if (cred->cr_ngroups < 2) {
575 #endif
576         pag = NOPAG;
577         goto out;
578     }
579 #endif
580 #if defined(AFS_AIX51_ENV)
581     g0 = cred->cr_groupset.gs_union.un_groups[0];
582     g1 = cred->cr_groupset.gs_union.un_groups[1];
583 #elif defined(AFS_LINUX26_ONEGROUP_ENV)
584 #elif defined(AFS_LINUX26_ENV)
585     g0 = GROUP_AT(cred->cr_group_info, 0);
586     g1 = GROUP_AT(cred->cr_group_info, 1);
587 #elif defined(AFS_SUN510_ENV)
588     g0 = gids[0];
589     g1 = gids[1];
590 #else
591     g0 = cred->cr_groups[0];
592     g1 = cred->cr_groups[1];
593 #endif
594 #endif
595 #if defined(AFS_LINUX26_ONEGROUP_ENV)
596     pag = (afs_int32) afs_get_pag_from_groups(cred->cr_group_info);
597 #else
598     pag = (afs_int32) afs_get_pag_from_groups(g0, g1);
599 #endif
600 out:
601 #if defined(AFS_LINUX26_ENV) && defined(LINUX_KEYRING_SUPPORT)
602     if (pag == NOPAG) {
603         struct key *key;
604         afs_uint32 upag, newpag;
605
606         key = request_key(&key_type_afs_pag, "_pag", NULL);
607         if (!IS_ERR(key)) {
608             if (key_validate(key) == 0 && key->uid == 0) {      /* also verify in the session keyring? */
609                 upag = (afs_uint32) key->payload.value;
610                 if (((upag >> 24) & 0xff) == 'A') {
611                     __setpag(&cred, upag, &newpag, 0);
612                     pag = (afs_int32) upag;
613                 }
614             }
615             key_put(key);
616         } 
617     }
618 #endif
619     return pag;
620 }