1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 * This file deals with PKCS #11 passwords and authentication.
51 /*************************************************************
52 * local static and global data
53 *************************************************************/
55 * This structure keeps track of status that spans all the Slots.
56 * NOTE: This is a global data structure. It semantics expect thread crosstalk
57 * be very careful when you see it used.
58 * It's major purpose in life is to allow the user to log in one PER
59 * Tranaction, even if a transaction spans threads. The problem is the user
60 * may have to enter a password one just to be able to look at the
61 * personalities/certificates (s)he can use. Then if Auth every is one, they
62 * may have to enter the password again to use the card. See PK11_StartTransac
63 * and PK11_EndTransaction.
65 static struct PK11GlobalStruct
{
68 char *(PR_CALLBACK
*getPass
)(PK11SlotInfo
*,PRBool
,void *);
69 PRBool (PR_CALLBACK
*verifyPass
)(PK11SlotInfo
*,void *);
70 PRBool (PR_CALLBACK
*isLoggedIn
)(PK11SlotInfo
*,void *);
71 } PK11_Global
= { 1, PR_FALSE
, NULL
, NULL
, NULL
};
73 /***********************************************************
75 ***********************************************************/
77 * Check the user's password. Log into the card if it's correct.
78 * succeed if the user is already logged in.
81 pk11_CheckPassword(PK11SlotInfo
*slot
,char *pw
)
86 int64 currtime
= PR_Now();
90 if (slot
->protectedAuthPath
) {
93 } else if (pw
== NULL
) {
94 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
97 len
= PORT_Strlen(pw
);
101 PK11_EnterSlotMonitor(slot
);
102 crv
= PK11_GETTAB(slot
)->C_Login(slot
->session
,CKU_USER
,
103 (unsigned char *)pw
,len
);
104 slot
->lastLoginCheck
= 0;
105 mustRetry
= PR_FALSE
;
106 PK11_ExitSlotMonitor(slot
);
108 /* if we're already logged in, we're good to go */
110 slot
->authTransact
= PK11_Global
.transaction
;
112 case CKR_USER_ALREADY_LOGGED_IN
:
113 slot
->authTime
= currtime
;
116 case CKR_PIN_INCORRECT
:
117 PORT_SetError(SEC_ERROR_BAD_PASSWORD
);
118 rv
= SECWouldBlock
; /* everything else is ok, only the pin is bad */
120 /* someone called reset while we fetched the password, try again once
121 * if the token is still there. */
122 case CKR_SESSION_HANDLE_INVALID
:
123 case CKR_SESSION_CLOSED
:
125 rv
= PK11_InitToken(slot
,PR_FALSE
);
126 if (rv
== SECSuccess
) {
127 if (slot
->session
!= CK_INVALID_SESSION
) {
130 PORT_SetError(PK11_MapError(crv
));
138 PORT_SetError(PK11_MapError(crv
));
139 rv
= SECFailure
; /* some failure we can't fix by retrying */
146 * Check the user's password. Logout before hand to make sure that
147 * we are really checking the password.
150 PK11_CheckUserPassword(PK11SlotInfo
*slot
, const char *pw
)
155 int64 currtime
= PR_Now();
157 if (slot
->protectedAuthPath
) {
160 } else if (pw
== NULL
) {
161 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
164 len
= PORT_Strlen(pw
);
168 PK11_EnterSlotMonitor(slot
);
169 PK11_GETTAB(slot
)->C_Logout(slot
->session
);
171 crv
= PK11_GETTAB(slot
)->C_Login(slot
->session
,CKU_USER
,
172 (unsigned char *)pw
,len
);
173 slot
->lastLoginCheck
= 0;
174 PK11_ExitSlotMonitor(slot
);
176 /* if we're already logged in, we're good to go */
178 slot
->authTransact
= PK11_Global
.transaction
;
179 slot
->authTime
= currtime
;
182 case CKR_PIN_INCORRECT
:
183 PORT_SetError(SEC_ERROR_BAD_PASSWORD
);
184 rv
= SECWouldBlock
; /* everything else is ok, only the pin is bad */
187 PORT_SetError(PK11_MapError(crv
));
188 rv
= SECFailure
; /* some failure we can't fix by retrying */
194 PK11_Logout(PK11SlotInfo
*slot
)
199 PK11_EnterSlotMonitor(slot
);
200 crv
= PK11_GETTAB(slot
)->C_Logout(slot
->session
);
201 slot
->lastLoginCheck
= 0;
202 PK11_ExitSlotMonitor(slot
);
204 PORT_SetError(PK11_MapError(crv
));
211 * transaction stuff is for when we test for the need to do every
212 * time auth to see if we already did it for this slot/transaction
214 void PK11_StartAuthTransaction(void)
216 PK11_Global
.transaction
++;
217 PK11_Global
.inTransaction
= PR_TRUE
;
220 void PK11_EndAuthTransaction(void)
222 PK11_Global
.transaction
++;
223 PK11_Global
.inTransaction
= PR_FALSE
;
227 * before we do a private key op, we check to see if we
228 * need to reauthenticate.
231 PK11_HandlePasswordCheck(PK11SlotInfo
*slot
,void *wincx
)
233 int askpw
= slot
->askpw
;
234 PRBool NeedAuth
= PR_FALSE
;
236 if (!slot
->needLogin
) return;
238 if ((slot
->defaultFlags
& PK11_OWN_PW_DEFAULTS
) == 0) {
239 PK11SlotInfo
*def_slot
= PK11_GetInternalKeySlot();
242 askpw
= def_slot
->askpw
;
243 PK11_FreeSlot(def_slot
);
247 /* timeouts are handled by isLoggedIn */
248 if (!PK11_IsLoggedIn(slot
,wincx
)) {
250 } else if (askpw
== -1) {
251 if (!PK11_Global
.inTransaction
||
252 (PK11_Global
.transaction
!= slot
->authTransact
)) {
253 PK11_EnterSlotMonitor(slot
);
254 PK11_GETTAB(slot
)->C_Logout(slot
->session
);
255 slot
->lastLoginCheck
= 0;
256 PK11_ExitSlotMonitor(slot
);
260 if (NeedAuth
) PK11_DoPassword(slot
,PR_TRUE
,wincx
);
264 PK11_SlotDBUpdate(PK11SlotInfo
*slot
)
266 SECMOD_UpdateModule(slot
->module
);
270 * set new askpw and timeout values
273 PK11_SetSlotPWValues(PK11SlotInfo
*slot
,int askpw
, int timeout
)
276 slot
->timeout
= timeout
;
277 slot
->defaultFlags
|= PK11_OWN_PW_DEFAULTS
;
278 PK11_SlotDBUpdate(slot
);
282 * Get the askpw and timeout values for this slot
285 PK11_GetSlotPWValues(PK11SlotInfo
*slot
,int *askpw
, int *timeout
)
287 *askpw
= slot
->askpw
;
288 *timeout
= slot
->timeout
;
290 if ((slot
->defaultFlags
& PK11_OWN_PW_DEFAULTS
) == 0) {
291 PK11SlotInfo
*def_slot
= PK11_GetInternalKeySlot();
294 *askpw
= def_slot
->askpw
;
295 *timeout
= def_slot
->timeout
;
296 PK11_FreeSlot(def_slot
);
302 * Returns true if the token is needLogin and isn't logged in.
303 * This function is used to determine if authentication is needed
304 * before attempting a potentially privelleged operation.
307 pk11_LoginStillRequired(PK11SlotInfo
*slot
, void *wincx
)
309 return slot
->needLogin
&& !PK11_IsLoggedIn(slot
,wincx
);
313 * make sure a slot is authenticated...
314 * This function only does the authentication if it is needed.
317 PK11_Authenticate(PK11SlotInfo
*slot
, PRBool loadCerts
, void *wincx
) {
318 if (pk11_LoginStillRequired(slot
,wincx
)) {
319 return PK11_DoPassword(slot
,loadCerts
,wincx
);
325 * Authenticate to "unfriendly" tokens (tokens which need to be logged
326 * in to find the certs.
329 pk11_AuthenticateUnfriendly(PK11SlotInfo
*slot
, PRBool loadCerts
, void *wincx
)
331 SECStatus rv
= SECSuccess
;
332 if (!PK11_IsFriendly(slot
)) {
333 rv
= PK11_Authenticate(slot
, loadCerts
, wincx
);
340 * NOTE: this assumes that we are logged out of the card before hand
343 PK11_CheckSSOPassword(PK11SlotInfo
*slot
, char *ssopw
)
345 CK_SESSION_HANDLE rwsession
;
347 SECStatus rv
= SECFailure
;
350 /* get a rwsession */
351 rwsession
= PK11_GetRWSession(slot
);
352 if (rwsession
== CK_INVALID_SESSION
) {
353 PORT_SetError(SEC_ERROR_BAD_DATA
);
357 if (slot
->protectedAuthPath
) {
360 } else if (ssopw
== NULL
) {
361 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
364 len
= PORT_Strlen(ssopw
);
367 /* check the password */
368 crv
= PK11_GETTAB(slot
)->C_Login(rwsession
,CKU_SO
,
369 (unsigned char *)ssopw
,len
);
370 slot
->lastLoginCheck
= 0;
372 /* if we're already logged in, we're good to go */
376 case CKR_PIN_INCORRECT
:
377 PORT_SetError(SEC_ERROR_BAD_PASSWORD
);
378 rv
= SECWouldBlock
; /* everything else is ok, only the pin is bad */
381 PORT_SetError(PK11_MapError(crv
));
382 rv
= SECFailure
; /* some failure we can't fix by retrying */
384 PK11_GETTAB(slot
)->C_Logout(rwsession
);
385 slot
->lastLoginCheck
= 0;
387 /* release rwsession */
388 PK11_RestoreROSession(slot
,rwsession
);
393 * make sure the password conforms to your token's requirements.
396 PK11_VerifyPW(PK11SlotInfo
*slot
,char *pw
)
398 int len
= PORT_Strlen(pw
);
400 if ((slot
->minPassword
> len
) || (slot
->maxPassword
< len
)) {
401 PORT_SetError(SEC_ERROR_BAD_DATA
);
408 * initialize a user PIN Value
411 PK11_InitPin(PK11SlotInfo
*slot
, const char *ssopw
, const char *userpw
)
413 CK_SESSION_HANDLE rwsession
= CK_INVALID_SESSION
;
415 SECStatus rv
= SECFailure
;
419 if (userpw
== NULL
) userpw
= "";
420 if (ssopw
== NULL
) ssopw
= "";
422 len
= PORT_Strlen(userpw
);
423 ssolen
= PORT_Strlen(ssopw
);
425 /* get a rwsession */
426 rwsession
= PK11_GetRWSession(slot
);
427 if (rwsession
== CK_INVALID_SESSION
) {
428 PORT_SetError(SEC_ERROR_BAD_DATA
);
429 slot
->lastLoginCheck
= 0;
433 if (slot
->protectedAuthPath
) {
440 /* check the password */
441 crv
= PK11_GETTAB(slot
)->C_Login(rwsession
,CKU_SO
,
442 (unsigned char *)ssopw
,ssolen
);
443 slot
->lastLoginCheck
= 0;
445 PORT_SetError(PK11_MapError(crv
));
449 crv
= PK11_GETTAB(slot
)->C_InitPIN(rwsession
,(unsigned char *)userpw
,len
);
451 PORT_SetError(PK11_MapError(crv
));
457 PK11_GETTAB(slot
)->C_Logout(rwsession
);
458 slot
->lastLoginCheck
= 0;
459 PK11_RestoreROSession(slot
,rwsession
);
460 if (rv
== SECSuccess
) {
461 /* update our view of the world */
462 PK11_InitToken(slot
,PR_TRUE
);
463 PK11_EnterSlotMonitor(slot
);
464 PK11_GETTAB(slot
)->C_Login(slot
->session
,CKU_USER
,
465 (unsigned char *)userpw
,len
);
466 slot
->lastLoginCheck
= 0;
467 PK11_ExitSlotMonitor(slot
);
473 * Change an existing user password
476 PK11_ChangePW(PK11SlotInfo
*slot
, const char *oldpw
, const char *newpw
)
479 SECStatus rv
= SECFailure
;
482 CK_SESSION_HANDLE rwsession
;
484 if (newpw
== NULL
) newpw
= "";
485 if (oldpw
== NULL
) oldpw
= "";
486 newLen
= PORT_Strlen(newpw
);
487 oldLen
= PORT_Strlen(oldpw
);
489 /* get a rwsession */
490 rwsession
= PK11_GetRWSession(slot
);
491 if (rwsession
== CK_INVALID_SESSION
) {
492 PORT_SetError(SEC_ERROR_BAD_DATA
);
496 crv
= PK11_GETTAB(slot
)->C_SetPIN(rwsession
,
497 (unsigned char *)oldpw
,oldLen
,(unsigned char *)newpw
,newLen
);
501 PORT_SetError(PK11_MapError(crv
));
504 PK11_RestoreROSession(slot
,rwsession
);
506 /* update our view of the world */
507 PK11_InitToken(slot
,PR_TRUE
);
512 pk11_GetPassword(PK11SlotInfo
*slot
, PRBool retry
, void * wincx
)
514 if (PK11_Global
.getPass
== NULL
) return NULL
;
515 return (*PK11_Global
.getPass
)(slot
, retry
, wincx
);
519 PK11_SetPasswordFunc(PK11PasswordFunc func
)
521 PK11_Global
.getPass
= func
;
525 PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func
)
527 PK11_Global
.verifyPass
= func
;
531 PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func
)
533 PK11_Global
.isLoggedIn
= func
;
538 * authenticate to a slot. This loops until we can't recover, the user
539 * gives up, or we succeed. If we're already logged in and this function
540 * is called we will still prompt for a password, but we will probably
541 * succeed no matter what the password was (depending on the implementation
542 * of the PKCS 11 module.
545 PK11_DoPassword(PK11SlotInfo
*slot
, PRBool loadCerts
, void *wincx
)
547 SECStatus rv
= SECFailure
;
549 PRBool attempt
= PR_FALSE
;
551 if (PK11_NeedUserInit(slot
)) {
552 PORT_SetError(SEC_ERROR_IO
);
558 * Central server type applications which control access to multiple
559 * slave applications to single crypto devices need to virtuallize the
560 * login state. This is done by a callback out of PK11_IsLoggedIn and
561 * here. If we are actually logged in, then we got here because the
562 * higher level code told us that the particular client application may
563 * still need to be logged in. If that is the case, we simply tell the
564 * server code that it should now verify the clients password and tell us
567 if (PK11_IsLoggedIn(slot
,NULL
) &&
568 (PK11_Global
.verifyPass
!= NULL
)) {
569 if (!PK11_Global
.verifyPass(slot
,wincx
)) {
570 PORT_SetError(SEC_ERROR_BAD_PASSWORD
);
576 /* get the password. This can drop out of the while loop
577 * for the following reasons:
578 * (1) the user refused to enter a password.
579 * (return error to caller)
580 * (2) the token user password is disabled [usually due to
581 * too many failed authentication attempts].
582 * (return error to caller)
583 * (3) the password was successful.
585 while ((password
= pk11_GetPassword(slot
, attempt
, wincx
)) != NULL
) {
586 /* if the token has a protectedAuthPath, the application may have
587 * already issued the C_Login as part of it's pk11_GetPassword call.
588 * In this case the application will tell us what the results were in
589 * the password value (retry or the authentication was successful) so
590 * we can skip our own C_Login call (which would force the token to
591 * try to login again).
593 * Applications that don't know about protectedAuthPath will return a
594 * password, which we will ignore and trigger the token to
595 * 'authenticate' itself anyway. Hopefully the blinking display on
596 * the reader, or the flashing light under the thumbprint reader will
597 * attract the user's attention */
599 if (slot
->protectedAuthPath
) {
600 /* application tried to authenticate and failed. it wants to try
601 * again, continue looping */
602 if (strcmp(password
, PK11_PW_RETRY
) == 0) {
607 /* applicaton tried to authenticate and succeeded we're done */
608 if (strcmp(password
, PK11_PW_AUTHENTICATED
) == 0) {
614 rv
= pk11_CheckPassword(slot
,password
);
615 PORT_Memset(password
, 0, PORT_Strlen(password
));
617 if (rv
!= SECWouldBlock
) break;
619 if (rv
== SECSuccess
) {
620 rv
= pk11_CheckVerifyTest(slot
);
621 if (!PK11_IsFriendly(slot
)) {
622 nssTrustDomain_UpdateCachedTokenCerts(slot
->nssToken
->trustDomain
,
625 } else if (!attempt
) PORT_SetError(SEC_ERROR_BAD_PASSWORD
);
629 void PK11_LogoutAll(void)
631 SECMODListLock
*lock
= SECMOD_GetDefaultModuleListLock();
632 SECMODModuleList
*modList
= SECMOD_GetDefaultModuleList();
633 SECMODModuleList
*mlp
= NULL
;
636 /* NSS is not initialized, there are not tokens to log out */
641 SECMOD_GetReadLock(lock
);
642 /* find the number of entries */
643 for (mlp
= modList
; mlp
!= NULL
; mlp
= mlp
->next
) {
644 for (i
=0; i
< mlp
->module
->slotCount
; i
++) {
645 PK11_Logout(mlp
->module
->slots
[i
]);
649 SECMOD_ReleaseReadLock(lock
);
653 PK11_GetMinimumPwdLength(PK11SlotInfo
*slot
)
655 return ((int)slot
->minPassword
);
658 /* Does this slot have a protected pin path? */
660 PK11_ProtectedAuthenticationPath(PK11SlotInfo
*slot
)
662 return slot
->protectedAuthPath
;
666 * we can initialize the password if 1) The toke is not inited
667 * (need login == true and see need UserInit) or 2) the token has
668 * a NULL password. (slot->needLogin = false & need user Init = false).
670 PRBool
PK11_NeedPWInitForSlot(PK11SlotInfo
*slot
)
672 if (slot
->needLogin
&& PK11_NeedUserInit(slot
)) {
675 if (!slot
->needLogin
&& !PK11_NeedUserInit(slot
)) {
681 PRBool
PK11_NeedPWInit()
683 PK11SlotInfo
*slot
= PK11_GetInternalKeySlot();
684 PRBool ret
= PK11_NeedPWInitForSlot(slot
);
691 pk11_InDelayPeriod(PRIntervalTime lastTime
, PRIntervalTime delayTime
,
692 PRIntervalTime
*retTime
)
696 *retTime
= time
= PR_IntervalNow();
697 return (PRBool
) (lastTime
) && ((time
-lastTime
) < delayTime
);
701 * Determine if the token is logged in. We have to actually query the token,
702 * because it's state can change without intervention from us.
705 PK11_IsLoggedIn(PK11SlotInfo
*slot
,void *wincx
)
707 CK_SESSION_INFO sessionInfo
;
708 int askpw
= slot
->askpw
;
709 int timeout
= slot
->timeout
;
711 PRIntervalTime curTime
;
712 static PRIntervalTime login_delay_time
= 0;
714 if (login_delay_time
== 0) {
715 login_delay_time
= PR_SecondsToInterval(1);
718 /* If we don't have our own password default values, use the system
720 if ((slot
->defaultFlags
& PK11_OWN_PW_DEFAULTS
) == 0) {
721 PK11SlotInfo
*def_slot
= PK11_GetInternalKeySlot();
724 askpw
= def_slot
->askpw
;
725 timeout
= def_slot
->timeout
;
726 PK11_FreeSlot(def_slot
);
730 if ((wincx
!= NULL
) && (PK11_Global
.isLoggedIn
!= NULL
) &&
731 (*PK11_Global
.isLoggedIn
)(slot
, wincx
) == PR_FALSE
) { return PR_FALSE
; }
734 /* forget the password if we've been inactive too long */
736 int64 currtime
= PR_Now();
740 LL_I2L(result
, timeout
);
741 LL_I2L(mult
, 60*1000*1000);
742 LL_MUL(result
,result
,mult
);
743 LL_ADD(result
, result
, slot
->authTime
);
744 if (LL_CMP(result
, <, currtime
) ) {
745 PK11_EnterSlotMonitor(slot
);
746 PK11_GETTAB(slot
)->C_Logout(slot
->session
);
747 slot
->lastLoginCheck
= 0;
748 PK11_ExitSlotMonitor(slot
);
750 slot
->authTime
= currtime
;
754 PK11_EnterSlotMonitor(slot
);
755 if (pk11_InDelayPeriod(slot
->lastLoginCheck
,login_delay_time
, &curTime
)) {
756 sessionInfo
.state
= slot
->lastState
;
759 crv
= PK11_GETTAB(slot
)->C_GetSessionInfo(slot
->session
,&sessionInfo
);
761 slot
->lastState
= sessionInfo
.state
;
762 slot
->lastLoginCheck
= curTime
;
765 PK11_ExitSlotMonitor(slot
);
766 /* if we can't get session info, something is really wrong */
768 slot
->session
= CK_INVALID_SESSION
;
772 switch (sessionInfo
.state
) {
773 case CKS_RW_PUBLIC_SESSION
:
774 case CKS_RO_PUBLIC_SESSION
:
777 case CKS_RW_USER_FUNCTIONS
:
778 case CKS_RW_SO_FUNCTIONS
:
779 case CKS_RO_USER_FUNCTIONS
: