Import Upstream version 1.8.5
[hcoop/debian/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_PAG_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 afs_uint32
538 afs_get_pag_from_groups(gid_t g0a, gid_t g1a)
539 {
540 afs_uint32 g0 = g0a;
541 afs_uint32 g1 = g1a;
542 afs_uint32 h, l, ret;
543
544 AFS_STATCNT(afs_get_pag_from_groups);
545
546 g0 -= 0x3f00;
547 g1 -= 0x3f00;
548 if (g0 < 0xc000 && g1 < 0xc000) {
549 l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
550 h = (g0 >> 14);
551 h = (g1 >> 14) + h + h + h;
552 ret = ((h << 28) | l);
553 # if defined(UKERNEL)
554 return ret;
555 # else
556 /* Additional testing */
557 if (((ret >> 24) & 0xff) == 'A')
558 return ret;
559 # endif /* UKERNEL */
560 }
561 return NOPAG;
562 }
563
564 #ifndef AFS_PAG_ONEGROUP_ENV
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
585 afs_get_groups_from_pag(afs_uint32 pag, gid_t *g0p, gid_t *g1p)
586 {
587 AFS_STATCNT(afs_get_groups_from_pag);
588 *g0p = pag;
589 *g1p = 0;
590 }
591 #endif
592
593 #ifdef AFS_LINUX26_ENV
594 /* osi_get_group_pag is defined in <ARCH>/osi_groups.c */
595 #elif defined(AFS_PAG_ONEGROUP_ENV)
596 /* osi_get_group_pag is defined in <ARCH>/osi_groups.c */
597 #elif defined(AFS_DARWIN110_ENV)
598 /* We don't have pags, so we do not define an osi_get_group_pag */
599 #else
600 static afs_int32
601 osi_get_group_pag(afs_ucred_t *cred)
602 {
603 afs_int32 pag = NOPAG;
604 gid_t g0, g1;
605 #if defined(AFS_SUN510_ENV)
606 const gid_t *gids;
607 int ngroups;
608 #endif
609
610 #if defined(AFS_SUN510_ENV)
611 gids = crgetgroups(cred);
612 ngroups = crgetngroups(cred);
613 #endif
614 #if defined(AFS_NBSD40_ENV)
615 if (cred == NOCRED || cred == FSCRED)
616 return NOPAG;
617 if (osi_crngroups(cred) < 3)
618 return NOPAG;
619 g0 = osi_crgroupbyid(cred, 1);
620 g1 = osi_crgroupbyid(cred, 2);
621 #elif defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
622 if (cred == NOCRED || cred == FSCRED)
623 return NOPAG;
624 if (cred->cr_ngroups < 3)
625 return NOPAG;
626 /* gid is stored in cr_groups[0] */
627 g0 = cred->cr_groups[1];
628 g1 = cred->cr_groups[2];
629 #else
630 # if defined(AFS_AIX_ENV)
631 if (cred->cr_ngrps < 2)
632 return NOPAG;
633 # elif defined(AFS_LINUX26_ENV)
634 if (afs_cr_group_info(cred)->ngroups < AFS_NUMPAGGROUPS)
635 return NOPAG;
636 # elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_XBSD_ENV)
637 # if defined(AFS_SUN510_ENV)
638 if (ngroups < 2) {
639 # else
640 if (cred->cr_ngroups < 2) {
641 # endif
642 return NOPAG;
643 }
644 # endif
645 # if defined(AFS_AIX51_ENV)
646 g0 = cred->cr_groupset.gs_union.un_groups[0];
647 g1 = cred->cr_groupset.gs_union.un_groups[1];
648 #elif defined(AFS_SUN510_ENV)
649 g0 = gids[0];
650 g1 = gids[1];
651 #else
652 g0 = cred->cr_groups[0];
653 g1 = cred->cr_groups[1];
654 #endif
655 #endif
656 pag = (afs_int32) afs_get_pag_from_groups(g0, g1);
657 return pag;
658 }
659 #endif
660
661
662 afs_int32
663 PagInCred(afs_ucred_t *cred)
664 {
665 afs_int32 pag = NOPAG;
666
667 AFS_STATCNT(PagInCred);
668 if (cred == NULL || cred == afs_osi_credp) {
669 return NOPAG;
670 }
671 #ifndef AFS_DARWIN110_ENV
672 #if defined(AFS_LINUX26_ENV) && defined(LINUX_KEYRING_SUPPORT)
673 /*
674 * If linux keyrings are in use and we carry the session keyring in our credentials
675 * structure, they should be the only criteria for determining
676 * if we're in a PAG. Groups are updated for legacy reasons only for now,
677 * and should not be used to infer PAG membership
678 * With keyrings but no kernel credentials, look at groups first and fall back
679 * to looking at the keyrings.
680 */
681 # if !defined(STRUCT_TASK_STRUCT_HAS_CRED)
682 pag = osi_get_group_pag(cred);
683 # endif
684 if (pag == NOPAG)
685 pag = osi_get_keyring_pag(cred);
686 #elif defined(AFS_AIX51_ENV)
687 if (kcred_getpag(cred, PAG_AFS, &pag) < 0 || pag == 0)
688 pag = NOPAG;
689 #else
690 pag = osi_get_group_pag(cred);
691 #endif
692 #endif
693 return pag;
694 }