Coverage Report

Created: 2024-11-21 07:03

/src/nss-nspr/nss/lib/dev/devslot.c
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "pkcs11.h"
6
7
#ifndef DEVM_H
8
#include "devm.h"
9
#endif /* DEVM_H */
10
11
#ifndef CKHELPER_H
12
#include "ckhelper.h"
13
#endif /* CKHELPER_H */
14
15
#include "pkim.h"
16
#include "dev3hack.h"
17
#include "pk11func.h"
18
19
/* measured in seconds */
20
0
#define NSSSLOT_TOKEN_DELAY_TIME 1
21
22
/* this should track global and per-transaction login information */
23
24
#define NSSSLOT_IS_FRIENDLY(slot) \
25
    (slot->base.flags & NSSSLOT_FLAGS_FRIENDLY)
26
27
/* measured as interval */
28
static PRIntervalTime s_token_delay_time = 0;
29
30
NSS_IMPLEMENT PRStatus
31
nssSlot_Destroy(
32
    NSSSlot *slot)
33
0
{
34
0
    if (slot) {
35
0
        if (PR_ATOMIC_DECREMENT(&slot->base.refCount) == 0) {
36
0
            PK11_FreeSlot(slot->pk11slot);
37
0
            PZ_DestroyLock(slot->base.lock);
38
0
            PZ_DestroyCondVar(slot->isPresentCondition);
39
0
            PZ_DestroyLock(slot->isPresentLock);
40
0
            return nssArena_Destroy(slot->base.arena);
41
0
        }
42
0
    }
43
0
    return PR_SUCCESS;
44
0
}
45
46
void
47
nssSlot_EnterMonitor(NSSSlot *slot)
48
0
{
49
0
    if (slot->lock) {
50
0
        PZ_Lock(slot->lock);
51
0
    }
52
0
}
53
54
void
55
nssSlot_ExitMonitor(NSSSlot *slot)
56
0
{
57
0
    if (slot->lock) {
58
0
        PZ_Unlock(slot->lock);
59
0
    }
60
0
}
61
62
NSS_IMPLEMENT void
63
NSSSlot_Destroy(
64
    NSSSlot *slot)
65
0
{
66
0
    (void)nssSlot_Destroy(slot);
67
0
}
68
69
NSS_IMPLEMENT NSSSlot *
70
nssSlot_AddRef(
71
    NSSSlot *slot)
72
0
{
73
0
    PR_ATOMIC_INCREMENT(&slot->base.refCount);
74
0
    return slot;
75
0
}
76
77
NSS_IMPLEMENT NSSUTF8 *
78
nssSlot_GetName(
79
    NSSSlot *slot)
80
0
{
81
0
    return slot->base.name;
82
0
}
83
84
NSS_IMPLEMENT void
85
nssSlot_ResetDelay(
86
    NSSSlot *slot)
87
0
{
88
0
    PZ_Lock(slot->isPresentLock);
89
0
    slot->lastTokenPingState = nssSlotLastPingState_Reset;
90
0
    PZ_Unlock(slot->isPresentLock);
91
0
}
92
93
static PRBool
94
token_status_checked(const NSSSlot *slot)
95
0
{
96
0
    PRIntervalTime time;
97
0
    int lastPingState = slot->lastTokenPingState;
98
    /* When called from the same thread, that means
99
     * nssSlot_IsTokenPresent() is called recursively through
100
     * nssSlot_Refresh(). Return immediately in that case. */
101
0
    if (slot->isPresentThread == PR_GetCurrentThread()) {
102
0
        return PR_TRUE;
103
0
    }
104
    /* Set the delay time for checking the token presence */
105
0
    if (s_token_delay_time == 0) {
106
0
        s_token_delay_time = PR_SecondsToInterval(NSSSLOT_TOKEN_DELAY_TIME);
107
0
    }
108
0
    time = PR_IntervalNow();
109
0
    if ((lastPingState == nssSlotLastPingState_Valid) && ((time - slot->lastTokenPingTime) < s_token_delay_time)) {
110
0
        return PR_TRUE;
111
0
    }
112
0
    return PR_FALSE;
113
0
}
114
115
NSS_IMPLEMENT PRBool
116
nssSlot_IsTokenPresent(
117
    NSSSlot *slot)
118
0
{
119
0
    CK_RV ckrv;
120
0
    PRStatus nssrv;
121
0
    NSSToken *nssToken = NULL;
122
    /* XXX */
123
0
    nssSession *session;
124
0
    CK_SLOT_INFO slotInfo;
125
0
    void *epv;
126
0
    PRBool isPresent = PR_FALSE;
127
0
    PRBool doUpdateCachedCerts = PR_FALSE;
128
129
    /* permanent slots are always present unless they're disabled */
130
0
    if (nssSlot_IsPermanent(slot)) {
131
0
        return !PK11_IsDisabled(slot->pk11slot);
132
0
    }
133
134
    /* avoid repeated calls to check token status within set interval */
135
0
    PZ_Lock(slot->isPresentLock);
136
0
    if (token_status_checked(slot)) {
137
0
        CK_FLAGS ckFlags = slot->ckFlags;
138
0
        PZ_Unlock(slot->isPresentLock);
139
0
        return ((ckFlags & CKF_TOKEN_PRESENT) != 0);
140
0
    }
141
0
    PZ_Unlock(slot->isPresentLock);
142
143
    /* First obtain the slot epv before we set up the condition
144
     * variable, so we can just return if we couldn't get it. */
145
0
    epv = slot->epv;
146
0
    if (!epv) {
147
0
        return PR_FALSE;
148
0
    }
149
150
    /* set up condition so only one thread is active in this part of the code at a time */
151
0
    PZ_Lock(slot->isPresentLock);
152
0
    while (slot->isPresentThread) {
153
0
        PR_WaitCondVar(slot->isPresentCondition, PR_INTERVAL_NO_TIMEOUT);
154
0
    }
155
    /* if we were one of multiple threads here, the first thread will have
156
     * given us the answer, no need to make more queries of the token. */
157
0
    if (token_status_checked(slot)) {
158
0
        CK_FLAGS ckFlags = slot->ckFlags;
159
0
        PZ_Unlock(slot->isPresentLock);
160
0
        return ((ckFlags & CKF_TOKEN_PRESENT) != 0);
161
0
    }
162
    /* this is the winning thread, block all others until we've determined
163
     * if the token is present and that it needs initialization. */
164
0
    slot->lastTokenPingState = nssSlotLastPingState_Update;
165
0
    slot->isPresentThread = PR_GetCurrentThread();
166
167
0
    PZ_Unlock(slot->isPresentLock);
168
169
0
    nssToken = PK11Slot_GetNSSToken(slot->pk11slot);
170
0
    if (!nssToken) {
171
0
        isPresent = PR_FALSE;
172
0
        goto done;
173
0
    }
174
175
0
    if (PK11_GetSlotInfo(slot->pk11slot, &slotInfo) != SECSuccess) {
176
0
        nssToken->base.name[0] = 0; /* XXX */
177
0
        isPresent = PR_FALSE;
178
0
        goto done;
179
0
    }
180
0
    slot->ckFlags = slotInfo.flags;
181
    /* check for the presence of the token */
182
0
    if ((slot->ckFlags & CKF_TOKEN_PRESENT) == 0) {
183
0
        session = nssToken_GetDefaultSession(nssToken);
184
0
        if (session) {
185
0
            nssSession_EnterMonitor(session);
186
            /* token is not present */
187
0
            if (session->handle != CK_INVALID_HANDLE) {
188
                /* session is valid, close and invalidate it */
189
0
                CKAPI(epv)
190
0
                    ->C_CloseSession(session->handle);
191
0
                session->handle = CK_INVALID_HANDLE;
192
0
            }
193
0
            nssSession_ExitMonitor(session);
194
0
        }
195
0
        if (nssToken->base.name[0] != 0) {
196
            /* notify the high-level cache that the token is removed */
197
0
            nssToken->base.name[0] = 0; /* XXX */
198
0
            nssToken_NotifyCertsNotVisible(nssToken);
199
0
        }
200
0
        nssToken->base.name[0] = 0; /* XXX */
201
        /* clear the token cache */
202
0
        nssToken_Remove(nssToken);
203
0
        isPresent = PR_FALSE;
204
0
        goto done;
205
0
    }
206
    /* token is present, use the session info to determine if the card
207
     * has been removed and reinserted.
208
     */
209
0
    session = nssToken_GetDefaultSession(nssToken);
210
0
    if (session) {
211
0
        PRBool tokenRemoved;
212
0
        nssSession_EnterMonitor(session);
213
0
        if (session->handle != CK_INVALID_HANDLE) {
214
0
            CK_SESSION_INFO sessionInfo;
215
0
            ckrv = CKAPI(epv)->C_GetSessionInfo(session->handle, &sessionInfo);
216
0
            if (ckrv != CKR_OK) {
217
                /* session is screwy, close and invalidate it */
218
0
                CKAPI(epv)
219
0
                    ->C_CloseSession(session->handle);
220
0
                session->handle = CK_INVALID_HANDLE;
221
0
            }
222
0
        }
223
0
        tokenRemoved = (session->handle == CK_INVALID_HANDLE);
224
0
        nssSession_ExitMonitor(session);
225
        /* token not removed, finished */
226
0
        if (!tokenRemoved) {
227
0
            isPresent = PR_TRUE;
228
0
            goto done;
229
0
        }
230
0
    }
231
    /* the token has been removed, and reinserted, or the slot contains
232
     * a token it doesn't recognize. invalidate all the old
233
     * information we had on this token, if we can't refresh, clear
234
     * the present flag */
235
0
    nssToken_NotifyCertsNotVisible(nssToken);
236
0
    nssToken_Remove(nssToken);
237
0
    if (nssToken->base.name[0] == 0) {
238
0
        doUpdateCachedCerts = PR_TRUE;
239
0
    }
240
0
    if (PK11_InitToken(slot->pk11slot, PR_FALSE) != SECSuccess) {
241
0
        isPresent = PR_FALSE;
242
0
        goto done;
243
0
    }
244
0
    if (doUpdateCachedCerts) {
245
0
        nssTrustDomain_UpdateCachedTokenCerts(nssToken->trustDomain,
246
0
                                              nssToken);
247
0
    }
248
0
    nssrv = nssToken_Refresh(nssToken);
249
0
    if (nssrv != PR_SUCCESS) {
250
0
        nssToken->base.name[0] = 0; /* XXX */
251
0
        slot->ckFlags &= ~CKF_TOKEN_PRESENT;
252
0
        isPresent = PR_FALSE;
253
0
        goto done;
254
0
    }
255
0
    isPresent = PR_TRUE;
256
0
done:
257
0
    if (nssToken) {
258
0
        (void)nssToken_Destroy(nssToken);
259
0
    }
260
    /* Once we've set up the condition variable,
261
     * Before returning, it's necessary to:
262
     *  1) Set the lastTokenPingTime so that any other threads waiting on this
263
     *     initialization and any future calls within the initialization window
264
     *     return the just-computed status.
265
     *  2) Indicate we're complete, waking up all other threads that may still
266
     *     be waiting on initialization can progress.
267
     */
268
0
    PZ_Lock(slot->isPresentLock);
269
    /* don't update the time if we were reset while we were
270
     * getting the token state */
271
0
    if (slot->lastTokenPingState == nssSlotLastPingState_Update) {
272
0
        slot->lastTokenPingTime = PR_IntervalNow();
273
0
        slot->lastTokenPingState = nssSlotLastPingState_Valid;
274
0
    }
275
0
    slot->isPresentThread = NULL;
276
0
    PR_NotifyAllCondVar(slot->isPresentCondition);
277
0
    PZ_Unlock(slot->isPresentLock);
278
0
    return isPresent;
279
0
}
280
281
NSS_IMPLEMENT void *
282
nssSlot_GetCryptokiEPV(
283
    NSSSlot *slot)
284
0
{
285
0
    return slot->epv;
286
0
}
287
288
NSS_IMPLEMENT NSSToken *
289
nssSlot_GetToken(
290
    NSSSlot *slot)
291
0
{
292
0
    NSSToken *rvToken = NULL;
293
294
0
    if (nssSlot_IsTokenPresent(slot)) {
295
0
        rvToken = PK11Slot_GetNSSToken(slot->pk11slot);
296
0
    }
297
298
0
    return rvToken;
299
0
}
300
301
NSS_IMPLEMENT PRStatus
302
nssSession_EnterMonitor(
303
    nssSession *s)
304
0
{
305
0
    if (s->lock)
306
0
        PZ_Lock(s->lock);
307
0
    return PR_SUCCESS;
308
0
}
309
310
NSS_IMPLEMENT PRStatus
311
nssSession_ExitMonitor(
312
    nssSession *s)
313
0
{
314
0
    return (s->lock) ? PZ_Unlock(s->lock) : PR_SUCCESS;
315
0
}
316
317
NSS_EXTERN PRBool
318
nssSession_IsReadWrite(
319
    nssSession *s)
320
0
{
321
0
    return s->isRW;
322
0
}