2 * Copyright 2000, International Business Machines Corporation and others.
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
13 #include <afsconfig.h>
14 #include "afs/param.h"
18 #include "afs/sysincludes.h" /* Standard vendor system headers */
21 #if !defined(AFS_LINUX20_ENV)
24 #include <netinet/in.h>
27 #include "h/hashing.h"
29 #if !defined(AFS_HPUX110_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV)
30 #include <netinet/in_var.h>
31 #endif /* ! AFS_HPUX110_ENV */
32 #endif /* !defined(UKERNEL) */
34 #include "afsincludes.h" /* Afs-based standard headers */
35 #include "afs/afs_stats.h" /* afs statistics */
37 #if defined(AFS_SUN5_ENV)
39 #include <inet/common.h>
40 #include <netinet/ip6.h>
44 #include "afs/afs_axscache.h"
46 /* Exported variables */
47 afs_rwlock_t afs_xuser
;
48 struct unixuser
*afs_users
[NUSERS
];
51 #ifndef AFS_PAG_MANAGER
52 /* Forward declarations */
53 void afs_ResetAccessCache(afs_int32 uid
, afs_int32 cell
, int alock
);
54 #endif /* !AFS_PAG_MANAGER */
57 /* Called from afs_Daemon to garbage collect unixusers no longer using system,
63 struct unixuser
*tu
, **lu
, *nu
;
65 afs_int32 now
, delFlag
;
67 AFS_STATCNT(afs_GCUserData
);
68 /* Obtain locks in valid order */
69 ObtainWriteLock(&afs_xuser
, 95);
70 #ifndef AFS_PAG_MANAGER
71 ObtainReadLock(&afs_xserver
);
72 ObtainWriteLock(&afs_xconn
, 96);
75 for (i
= 0; i
< NUSERS
; i
++) {
76 for (lu
= &afs_users
[i
], tu
= *lu
; tu
; tu
= nu
) {
77 delFlag
= 0; /* should we delete this dude? */
78 /* Don't garbage collect users in use now (refCount) */
79 if (tu
->refCount
== 0) {
81 /* Need to walk the token stack, and dispose of
82 * all expired tokens */
83 afs_DiscardExpiredTokens(&tu
->tokens
, now
);
84 if (!afs_HasUsableTokens(tu
->tokens
, now
))
87 if (tu
->tokenTime
< now
- NOTOKTIMEOUT
)
94 #ifndef AFS_PAG_MANAGER
95 afs_ReleaseConnsUser(tu
);
97 afs_FreeTokens(&tu
->tokens
);
100 EXP_RELE(tu
->exporter
);
101 afs_osi_Free(tu
, sizeof(struct unixuser
));
107 #ifndef AFS_PAG_MANAGER
108 ReleaseWriteLock(&afs_xconn
);
110 #ifndef AFS_PAG_MANAGER
111 ReleaseReadLock(&afs_xserver
);
113 ReleaseWriteLock(&afs_xuser
);
115 } /*afs_GCUserData */
117 static struct unixuser
*
118 afs_FindUserNoLock(afs_int32 auid
, afs_int32 acell
)
123 AFS_STATCNT(afs_FindUser
);
125 for (tu
= afs_users
[i
]; tu
; tu
= tu
->next
) {
126 if (tu
->uid
== auid
&& ((tu
->cell
== acell
) || (acell
== -1))) {
135 #ifndef AFS_PAG_MANAGER
137 * Check for unixusers who encountered bad tokens, and reset the access
138 * cache for these guys. Can't do this when token expiration detected,
139 * since too many locks are set then.
142 afs_CheckTokenCache(void)
148 struct axscache
*tofreelist
;
151 AFS_STATCNT(afs_CheckCacheResets
);
152 ObtainReadLock(&afs_xvcache
);
153 ObtainReadLock(&afs_xuser
);
155 for (i
= 0; i
< NUSERS
; i
++) {
156 for (tu
= afs_users
[i
]; tu
; tu
= tu
->next
) {
158 * If tokens are still good and user has Kerberos tickets,
161 if ((tu
->states
& UHasTokens
) && !(tu
->states
& UTokensBad
)) {
162 if (!afs_HasUsableTokens(tu
->tokens
, now
)) {
164 * This token has expired, warn users and reset access
167 tu
->states
|= (UTokensBad
| UNeedsReset
);
170 if (tu
->states
& UNeedsReset
)
174 /* Skip the potentially expensive scan if nothing to do */
179 for (i
= 0; i
< VCSIZE
; i
++) {
180 for (tvc
= afs_vhashT
[i
]; tvc
; tvc
= tvc
->hnext
) {
181 /* really should do this under cache write lock, but that.
182 * is hard to under locking hierarchy */
184 struct axscache
**ac
, **nac
;
186 for ( ac
= &tvc
->Access
; *ac
;) {
188 tu
= afs_FindUserNoLock((*ac
)->uid
, tvc
->f
.fid
.Cell
);
189 if (tu
== NULL
|| (tu
->states
& UNeedsReset
)) {
190 struct axscache
*tmp
;
193 tmp
->next
= tofreelist
;
203 afs_FreeAllAxs(&tofreelist
);
204 for (i
= 0; i
< NUSERS
; i
++) {
205 for (tu
= afs_users
[i
]; tu
; tu
= tu
->next
) {
206 if (tu
->states
& UNeedsReset
)
207 tu
->states
&= ~UNeedsReset
;
212 ReleaseReadLock(&afs_xuser
);
213 ReleaseReadLock(&afs_xvcache
);
214 } /*afs_CheckTokenCache */
217 /* Remove any access caches associated with this uid+cell
218 * by scanning the entire vcache table. Specify cell=-1
219 * to remove all access caches associated with this uid
220 * regardless of cell.
223 afs_ResetAccessCache(afs_int32 uid
, afs_int32 cell
, int alock
)
229 AFS_STATCNT(afs_ResetAccessCache
);
231 ObtainReadLock(&afs_xvcache
);
232 for (i
= 0; i
< VCSIZE
; i
++) {
233 for (tvc
= afs_vhashT
[i
]; tvc
; tvc
= tvc
->hnext
) {
234 /* really should do this under cache write lock, but that.
235 * is hard to under locking hierarchy */
236 if (tvc
->Access
&& (cell
== -1 || tvc
->f
.fid
.Cell
== cell
)) {
237 ac
= afs_FindAxs(tvc
->Access
, uid
);
239 afs_RemoveAxs(&tvc
->Access
, ac
);
245 ReleaseReadLock(&afs_xvcache
);
247 } /*afs_ResetAccessCache */
251 * Ensure all connections make use of new tokens. Discard incorrectly-cached
255 afs_ResetUserConns(struct unixuser
*auser
)
259 struct sa_conn_vector
*tcv
;
261 AFS_STATCNT(afs_ResetUserConns
);
262 ObtainReadLock(&afs_xsrvAddr
);
263 ObtainWriteLock(&afs_xconn
, 98);
265 for (i
= 0; i
< NSERVERS
; i
++) {
266 for (sa
= afs_srvAddrs
[i
]; sa
; sa
= sa
->next_bkt
) {
267 for (tcv
= sa
->conns
; tcv
; tcv
= tcv
->next
) {
268 if (tcv
->user
== auser
) {
269 for(j
= 0; j
< CVEC_LEN
; ++j
) {
270 (tcv
->cvec
[j
]).forceConnectFS
= 1;
277 ReleaseWriteLock(&afs_xconn
);
278 ReleaseReadLock(&afs_xsrvAddr
);
279 afs_ResetAccessCache(auser
->uid
, auser
->cell
, 1);
280 auser
->states
&= ~UNeedsReset
;
281 } /*afs_ResetUserConns */
282 #endif /* !AFS_PAG_MANAGER */
286 afs_FindUser(afs_int32 auid
, afs_int32 acell
, afs_int32 locktype
)
290 ObtainWriteLock(&afs_xuser
, 99);
291 tu
= afs_FindUserNoLock(auid
, acell
);
292 ReleaseWriteLock(&afs_xuser
);
294 afs_LockUser(tu
, locktype
, 365);
299 /*------------------------------------------------------------------------
300 * EXPORTED afs_ComputePAGStats
303 * Compute a set of stats concerning PAGs used by this machine.
312 * The results are put in the structure responsible for keeping
313 * detailed CM stats. Note: entries corresponding to a single PAG
314 * will appear on the identical hash chain, so sweeping the chain
315 * will find all entries related to a single PAG.
319 *------------------------------------------------------------------------*/
322 afs_ComputePAGStats(void)
324 struct unixuser
*currPAGP
; /*Ptr to curr PAG */
325 struct unixuser
*cmpPAGP
; /*Ptr to PAG being compared */
326 struct afs_stats_AuthentInfo
*authP
; /*Ptr to stats area */
327 int curr_Record
; /*Curr record */
328 int currChain
; /*Curr hash chain */
329 int currChainLen
; /*Length of curr hash chain */
330 int currPAGRecords
; /*# records in curr PAG */
333 * Lock out everyone else from scribbling on the PAG entries.
335 ObtainReadLock(&afs_xuser
);
338 * Initialize the tallies, then sweep through each hash chain. We
339 * can't bzero the structure, since some fields are cumulative over
343 authP
= &(afs_stats_cmfullperf
.authent
);
344 authP
->curr_PAGs
= 0;
345 authP
->curr_Records
= 0;
346 authP
->curr_AuthRecords
= 0;
347 authP
->curr_UnauthRecords
= 0;
348 authP
->curr_MaxRecordsInPAG
= 0;
349 authP
->curr_LongestChain
= 0;
351 for (currChain
= 0; currChain
< NUSERS
; currChain
++) {
353 for (currPAGP
= afs_users
[currChain
]; currPAGP
;
354 currPAGP
= currPAGP
->next
) {
356 * Bump the number of records on this current chain, along with
357 * the total number of records in existence.
362 * We've found a previously-uncounted PAG. If it's been deleted
363 * but just not garbage-collected yet, we step over it.
365 if (!(currPAGP
->states
& UHasTokens
))
369 * If this PAG record has already been ``counted', namely
370 * associated with a PAG and tallied, clear its bit and move on.
372 (authP
->curr_Records
)++;
373 if (currPAGP
->states
& UPAGCounted
) {
374 currPAGP
->states
&= ~UPAGCounted
;
380 /*We've counted this one already */
382 * Jot initial info down, then sweep down the rest of the hash
383 * chain, looking for matching PAG entries. Note: to properly
384 * ``count'' the current record, we first compare it to itself
385 * in the following loop.
387 (authP
->curr_PAGs
)++;
390 for (cmpPAGP
= currPAGP
; cmpPAGP
; cmpPAGP
= cmpPAGP
->next
) {
391 if (currPAGP
->uid
== cmpPAGP
->uid
) {
393 * The records belong to the same PAG. First, mark the
394 * new record as ``counted'' and bump the PAG size.
395 * Then, record the state of its ticket, if any.
397 cmpPAGP
->states
|= UPAGCounted
;
399 if ((cmpPAGP
->states
& UHasTokens
)
400 && !(cmpPAGP
->states
& UTokensBad
))
401 (authP
->curr_AuthRecords
)++;
403 (authP
->curr_UnauthRecords
)++;
404 } /*Records belong to same PAG */
405 } /*Compare to rest of PAG records in chain */
408 * In the above comparisons, the current PAG record has been
409 * marked as counted. Erase this mark before moving on.
411 currPAGP
->states
&= ~UPAGCounted
;
414 * We've compared our current PAG record with all remaining
415 * PAG records in the hash chain. Update our tallies, and
416 * perhaps even our lifetime high water marks. After that,
417 * remove our search mark and advance to the next comparison
420 if (currPAGRecords
> authP
->curr_MaxRecordsInPAG
) {
421 authP
->curr_MaxRecordsInPAG
= currPAGRecords
;
422 if (currPAGRecords
> authP
->HWM_MaxRecordsInPAG
)
423 authP
->HWM_MaxRecordsInPAG
= currPAGRecords
;
425 } /*Sweep a hash chain */
428 * If the chain we just finished zipping through is the longest we've
429 * seen yet, remember this fact before advancing to the next chain.
431 if (currChainLen
> authP
->curr_LongestChain
) {
432 authP
->curr_LongestChain
= currChainLen
;
433 if (currChainLen
> authP
->HWM_LongestChain
)
434 authP
->HWM_LongestChain
= currChainLen
;
437 } /*For each hash chain in afs_user */
440 * Now that we've counted everything up, we can consider all-time
443 if (authP
->curr_PAGs
> authP
->HWM_PAGs
)
444 authP
->HWM_PAGs
= authP
->curr_PAGs
;
445 if (authP
->curr_Records
> authP
->HWM_Records
)
446 authP
->HWM_Records
= authP
->curr_Records
;
449 * People are free to manipulate the PAG structures now.
451 ReleaseReadLock(&afs_xuser
);
453 } /*afs_ComputePAGStats */
456 * Obtain a unixuser for the specified uid and cell;
457 * if no existing match found, allocate a new one.
459 * \param[in] auid uid/PAG value
460 * \param[in] acell cell number; if -1, match on auid only
461 * \param[in] locktype locktype desired on returned unixuser
463 * \post unixuser is chained in afs_users[], returned with <locktype> held
465 * \note Maintain unixusers in sorted order within hash bucket to enable
466 * small lookup optimizations.
470 afs_GetUser(afs_int32 auid
, afs_int32 acell
, afs_int32 locktype
)
472 struct unixuser
*tu
, *xu
= 0, *pu
= 0;
474 afs_int32 RmtUser
= 0;
476 AFS_STATCNT(afs_GetUser
);
478 ObtainWriteLock(&afs_xuser
, 104);
479 /* unixusers are sorted by uid in each hash bucket */
480 for (tu
= afs_users
[i
]; tu
&& (tu
->uid
<= auid
) ; xu
= tu
, tu
= tu
->next
) {
481 if (tu
->uid
== auid
) {
488 if (tu
->cell
== -1 && acell
!= -1) {
489 /* Here we setup the real cell for the client */
493 } else if (tu
->cell
== acell
|| acell
== -1) {
499 /* no matching unixuser found; repurpose the tu pointer to
500 * allocate a new unixuser.
501 * xu will be insertion point for our new unixuser.
503 tu
= afs_osi_Alloc(sizeof(struct unixuser
));
504 osi_Assert(tu
!= NULL
);
506 afs_stats_cmfullperf
.authent
.PAGCreations
++;
507 #endif /* AFS_NOSTATS */
508 memset(tu
, 0, sizeof(struct unixuser
));
509 AFS_RWLOCK_INIT(&tu
->lock
, "unixuser lock");
510 /* insert new nu in sorted order after xu */
512 tu
->next
= afs_users
[i
];
520 * This is for the case where an additional unixuser struct is
521 * created because the remote client is accessing a different cell;
522 * we simply rerecord relevant information from the original
525 if (pu
&& pu
->exporter
) {
526 tu
->exporter
= pu
->exporter
;
527 (void)EXP_HOLD(tu
->exporter
);
532 tu
->viceId
= UNDEFVID
;
534 tu
->tokenTime
= osi_Time();
535 /* fall through to return the new one */
538 ReleaseWriteLock(&afs_xuser
);
539 afs_LockUser(tu
, locktype
, 364);
545 afs_LockUser(struct unixuser
*au
, afs_int32 locktype
,
546 unsigned int src_indicator
)
550 ObtainReadLock(&au
->lock
);
553 ObtainWriteLock(&au
->lock
, src_indicator
);
556 ObtainSharedLock(&au
->lock
, src_indicator
);
565 afs_PutUser(struct unixuser
*au
, afs_int32 locktype
)
567 AFS_STATCNT(afs_PutUser
);
571 ReleaseReadLock(&au
->lock
);
574 ReleaseWriteLock(&au
->lock
);
577 ReleaseSharedLock(&au
->lock
);
589 * Set the primary flag on a unixuser structure, ensuring that exactly one
590 * dude has the flag set at any time for a particular unix uid.
593 afs_SetPrimary(struct unixuser
*au
, int aflag
)
599 AFS_STATCNT(afs_SetPrimary
);
602 ObtainWriteLock(&afs_xuser
, 105);
604 * See if anyone is this uid's primary cell yet; recording in pu the
607 for (tu
= afs_users
[i
]; tu
; tu
= tu
->next
) {
608 if (tu
->uid
== au
->uid
&& (tu
->states
& UPrimary
)) {
612 if (pu
&& !(pu
->states
& UHasTokens
)) {
614 * Primary user has unlogged, don't treat him as primary any longer;
615 * note that we want to treat him as primary until now, so that
616 * people see a primary identity until now.
618 pu
->states
&= ~UPrimary
;
622 /* setting au to be primary */
624 pu
->states
&= ~UPrimary
;
625 au
->states
|= UPrimary
;
626 } else if (aflag
== 0) {
627 /* we don't know if we're supposed to be primary or not */
628 if (!pu
|| au
== pu
) {
629 au
->states
|= UPrimary
;
631 au
->states
&= ~UPrimary
;
633 ReleaseWriteLock(&afs_xuser
);
635 } /*afs_SetPrimary */
638 afs_NotifyUser(struct unixuser
*auser
, int event
)
640 #ifdef AFS_DARWIN_ENV
641 darwin_notify_perms(auser
, event
);
646 * Mark all of the unixuser records held for a particular PAG as
650 * PAG to expire records for
653 afs_MarkUserExpired(afs_int32 pag
)
659 ObtainWriteLock(&afs_xuser
, 9);
660 for (tu
= afs_users
[i
]; tu
; tu
= tu
->next
) {
661 if (tu
->uid
== pag
) {
662 tu
->states
&= ~UHasTokens
;
666 ReleaseWriteLock(&afs_xuser
);
673 * Called by osi_TraverseProcTable (from afs_GCPAGs) for each
674 * process in the system.
675 * If the specified process uses a PAG, clear that PAG's temporary
680 * This variable keeps track of the number of UID-base
681 * tokens in the afs_users table. When it's zero
682 * the per process loop in GCPAGs doesn't have to
683 * check processes without pags against the afs_users table.
685 static afs_int32 afs_GCPAGs_UIDBaseTokenCount
= 0;
688 * These variables keep track of the number of times
689 * afs_GCPAGs_perproc_func() is called. If it is not called at all when
690 * walking the process table, there is something wrong and we should not
691 * prematurely expire any tokens.
693 static size_t afs_GCPAGs_perproc_count
= 0;
694 static size_t afs_GCPAGs_cred_count
= 0;
697 * LOCKS: afs_GCPAGs_perproc_func requires write lock on afs_xuser
699 #if !defined(LINUX_KEYRING_SUPPORT) && (!defined(STRUCT_TASK_STRUCT_HAS_CRED) || defined(HAVE_LINUX_RCU_READ_LOCK))
701 afs_GCPAGs_perproc_func(afs_proc_t
* pproc
)
703 afs_int32 pag
, hash
, uid
;
704 const afs_ucred_t
*pcred
;
706 afs_GCPAGs_perproc_count
++;
708 pcred
= afs_osi_proc2cred(pproc
);
712 afs_GCPAGs_cred_count
++;
714 pag
= PagInCred(pcred
);
715 #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_LINUX22_ENV)
716 uid
= (pag
!= NOPAG
? pag
: afs_cr_uid(pcred
));
717 #elif defined(AFS_SUN510_ENV)
718 uid
= (pag
!= NOPAG
? pag
: crgetruid(pcred
));
720 uid
= (pag
!= NOPAG
? pag
: afs_cr_ruid(pcred
));
724 /* if this token is PAG based, or it's UID based and
725 * UID-based tokens exist */
726 if ((pag
!= NOPAG
) || (afs_GCPAGs_UIDBaseTokenCount
)) {
727 /* find the entries for this uid in all cells and clear the not
728 * referenced flag. Can't use afs_FindUser, because it just returns
729 * the specific cell asked for, or the first one found.
732 for (pu
= afs_users
[hash
]; pu
; pu
= pu
->next
) {
733 if (pu
->uid
== uid
) {
734 if (pu
->states
& TMP_UPAGNotReferenced
) {
735 /* clear the 'deleteme' flag for this entry */
736 pu
->states
&= ~TMP_UPAGNotReferenced
;
738 /* This is a uid based token that hadn't
739 * previously been cleared, so decrement the
740 * outstanding uid based token count */
741 afs_GCPAGs_UIDBaseTokenCount
--;
751 * Go through the process table, find all unused PAGs
752 * and cause them to be deleted during the next GC.
754 * returns the number of PAGs marked for deletion
756 * On AIX we free PAGs when the last accessing process exits,
757 * so this routine is not needed.
759 * In AFS WebSecure, we explicitly call unlog when we remove
760 * an entry in the login cache, so this routine is not needed.
764 afs_GCPAGs(afs_int32
* ReleasedCount
)
769 if (afs_gcpags
!= AFS_GCPAGS_OK
) {
775 /* first, loop through afs_users, setting the temporary 'deleteme' flag */
776 ObtainWriteLock(&afs_xuser
, 419);
777 afs_GCPAGs_UIDBaseTokenCount
= 0;
778 for (i
= 0; i
< NUSERS
; i
++) {
779 for (pu
= afs_users
[i
]; pu
; pu
= pu
->next
) {
780 pu
->states
|= TMP_UPAGNotReferenced
;
781 if (((pu
->uid
>> 24) & 0xff) != 'A') {
782 /* this is a uid-based token, */
783 /* increment the count */
784 afs_GCPAGs_UIDBaseTokenCount
++;
789 /* Now, iterate through the systems process table,
790 * for each process, mark it's PAGs (if any) in use.
791 * i.e. clear the temporary deleteme flag.
793 afs_GCPAGs_perproc_count
= 0;
794 afs_GCPAGs_cred_count
= 0;
796 afs_osi_TraverseProcTable();
798 /* If there is an internal problem and afs_GCPAGs_perproc_func()
799 * does not get called, disable gcpags so that we do not
800 * accidentally expire all the tokens in the system.
802 if (afs_gcpags
== AFS_GCPAGS_OK
&& !afs_GCPAGs_perproc_count
) {
803 afs_gcpags
= AFS_GCPAGS_EPROCWALK
;
806 if (afs_gcpags
== AFS_GCPAGS_OK
&& !afs_GCPAGs_cred_count
) {
807 afs_gcpags
= AFS_GCPAGS_ECREDWALK
;
810 /* Now, go through afs_users again, any that aren't in use
811 * (temp deleteme flag still set) will be marked for later deletion,
812 * by setting their expire times to 0.
814 for (i
= 0; i
< NUSERS
; i
++) {
815 for (pu
= afs_users
[i
]; pu
; pu
= pu
->next
) {
816 if (pu
->states
& TMP_UPAGNotReferenced
) {
818 /* clear the temp flag */
819 pu
->states
&= ~TMP_UPAGNotReferenced
;
821 /* Is this entry on behalf of a 'remote' user ?
822 * i.e. nfs translator, etc.
824 if (!pu
->exporter
&& afs_gcpags
== AFS_GCPAGS_OK
) {
825 /* make afs_GCUserData remove this entry */
826 pu
->states
&= ~UHasTokens
;
829 (*ReleasedCount
)++; /* remember how many we marked (info only) */
835 ReleaseWriteLock(&afs_xuser
);
840 #endif /* AFS_GCPAGS */