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