security/nss/lib/pk11wrap/pk11auth.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 /*
michael@0 5 * This file deals with PKCS #11 passwords and authentication.
michael@0 6 */
michael@0 7 #include "seccomon.h"
michael@0 8 #include "secmod.h"
michael@0 9 #include "secmodi.h"
michael@0 10 #include "secmodti.h"
michael@0 11 #include "pkcs11t.h"
michael@0 12 #include "pk11func.h"
michael@0 13 #include "secitem.h"
michael@0 14 #include "secerr.h"
michael@0 15
michael@0 16 #include "pkim.h"
michael@0 17
michael@0 18
michael@0 19 /*************************************************************
michael@0 20 * local static and global data
michael@0 21 *************************************************************/
michael@0 22 /*
michael@0 23 * This structure keeps track of status that spans all the Slots.
michael@0 24 * NOTE: This is a global data structure. It semantics expect thread crosstalk
michael@0 25 * be very careful when you see it used.
michael@0 26 * It's major purpose in life is to allow the user to log in one PER
michael@0 27 * Tranaction, even if a transaction spans threads. The problem is the user
michael@0 28 * may have to enter a password one just to be able to look at the
michael@0 29 * personalities/certificates (s)he can use. Then if Auth every is one, they
michael@0 30 * may have to enter the password again to use the card. See PK11_StartTransac
michael@0 31 * and PK11_EndTransaction.
michael@0 32 */
michael@0 33 static struct PK11GlobalStruct {
michael@0 34 int transaction;
michael@0 35 PRBool inTransaction;
michael@0 36 char *(PR_CALLBACK *getPass)(PK11SlotInfo *,PRBool,void *);
michael@0 37 PRBool (PR_CALLBACK *verifyPass)(PK11SlotInfo *,void *);
michael@0 38 PRBool (PR_CALLBACK *isLoggedIn)(PK11SlotInfo *,void *);
michael@0 39 } PK11_Global = { 1, PR_FALSE, NULL, NULL, NULL };
michael@0 40
michael@0 41 /***********************************************************
michael@0 42 * Password Utilities
michael@0 43 ***********************************************************/
michael@0 44 /*
michael@0 45 * Check the user's password. Log into the card if it's correct.
michael@0 46 * succeed if the user is already logged in.
michael@0 47 */
michael@0 48 static SECStatus
michael@0 49 pk11_CheckPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
michael@0 50 char *pw, PRBool alreadyLocked, PRBool contextSpecific)
michael@0 51 {
michael@0 52 int len = 0;
michael@0 53 CK_RV crv;
michael@0 54 SECStatus rv;
michael@0 55 PRTime currtime = PR_Now();
michael@0 56 PRBool mustRetry;
michael@0 57 int retry = 0;
michael@0 58
michael@0 59 if (slot->protectedAuthPath) {
michael@0 60 len = 0;
michael@0 61 pw = NULL;
michael@0 62 } else if (pw == NULL) {
michael@0 63 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 64 return SECFailure;
michael@0 65 } else {
michael@0 66 len = PORT_Strlen(pw);
michael@0 67 }
michael@0 68
michael@0 69 do {
michael@0 70 if (!alreadyLocked) PK11_EnterSlotMonitor(slot);
michael@0 71 crv = PK11_GETTAB(slot)->C_Login(session,
michael@0 72 contextSpecific ? CKU_CONTEXT_SPECIFIC : CKU_USER,
michael@0 73 (unsigned char *)pw,len);
michael@0 74 slot->lastLoginCheck = 0;
michael@0 75 mustRetry = PR_FALSE;
michael@0 76 if (!alreadyLocked) PK11_ExitSlotMonitor(slot);
michael@0 77 switch (crv) {
michael@0 78 /* if we're already logged in, we're good to go */
michael@0 79 case CKR_OK:
michael@0 80 /* TODO If it was for CKU_CONTEXT_SPECIFIC should we do this */
michael@0 81 slot->authTransact = PK11_Global.transaction;
michael@0 82 /* Fall through */
michael@0 83 case CKR_USER_ALREADY_LOGGED_IN:
michael@0 84 slot->authTime = currtime;
michael@0 85 rv = SECSuccess;
michael@0 86 break;
michael@0 87 case CKR_PIN_INCORRECT:
michael@0 88 PORT_SetError(SEC_ERROR_BAD_PASSWORD);
michael@0 89 rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
michael@0 90 break;
michael@0 91 /* someone called reset while we fetched the password, try again once
michael@0 92 * if the token is still there. */
michael@0 93 case CKR_SESSION_HANDLE_INVALID:
michael@0 94 case CKR_SESSION_CLOSED:
michael@0 95 if (session != slot->session) {
michael@0 96 /* don't bother retrying, we were in a middle of an operation,
michael@0 97 * which is now lost. Just fail. */
michael@0 98 PORT_SetError(PK11_MapError(crv));
michael@0 99 rv = SECFailure;
michael@0 100 break;
michael@0 101 }
michael@0 102 if (retry++ == 0) {
michael@0 103 rv = PK11_InitToken(slot,PR_FALSE);
michael@0 104 if (rv == SECSuccess) {
michael@0 105 if (slot->session != CK_INVALID_SESSION) {
michael@0 106 session = slot->session; /* we should have
michael@0 107 * a new session now */
michael@0 108 mustRetry = PR_TRUE;
michael@0 109 } else {
michael@0 110 PORT_SetError(PK11_MapError(crv));
michael@0 111 rv = SECFailure;
michael@0 112 }
michael@0 113 }
michael@0 114 break;
michael@0 115 }
michael@0 116 /* Fall through */
michael@0 117 default:
michael@0 118 PORT_SetError(PK11_MapError(crv));
michael@0 119 rv = SECFailure; /* some failure we can't fix by retrying */
michael@0 120 }
michael@0 121 } while (mustRetry);
michael@0 122 return rv;
michael@0 123 }
michael@0 124
michael@0 125 /*
michael@0 126 * Check the user's password. Logout before hand to make sure that
michael@0 127 * we are really checking the password.
michael@0 128 */
michael@0 129 SECStatus
michael@0 130 PK11_CheckUserPassword(PK11SlotInfo *slot, const char *pw)
michael@0 131 {
michael@0 132 int len = 0;
michael@0 133 CK_RV crv;
michael@0 134 SECStatus rv;
michael@0 135 PRTime currtime = PR_Now();
michael@0 136
michael@0 137 if (slot->protectedAuthPath) {
michael@0 138 len = 0;
michael@0 139 pw = NULL;
michael@0 140 } else if (pw == NULL) {
michael@0 141 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 142 return SECFailure;
michael@0 143 } else {
michael@0 144 len = PORT_Strlen(pw);
michael@0 145 }
michael@0 146
michael@0 147 /*
michael@0 148 * If the token doesn't need a login, don't try to relogin because the
michael@0 149 * effect is undefined. It's not clear what it means to check a non-empty
michael@0 150 * password with such a token, so treat that as an error.
michael@0 151 */
michael@0 152 if (!slot->needLogin) {
michael@0 153 if (len == 0) {
michael@0 154 rv = SECSuccess;
michael@0 155 } else {
michael@0 156 PORT_SetError(SEC_ERROR_BAD_PASSWORD);
michael@0 157 rv = SECFailure;
michael@0 158 }
michael@0 159 return rv;
michael@0 160 }
michael@0 161
michael@0 162 /* force a logout */
michael@0 163 PK11_EnterSlotMonitor(slot);
michael@0 164 PK11_GETTAB(slot)->C_Logout(slot->session);
michael@0 165
michael@0 166 crv = PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER,
michael@0 167 (unsigned char *)pw,len);
michael@0 168 slot->lastLoginCheck = 0;
michael@0 169 PK11_ExitSlotMonitor(slot);
michael@0 170 switch (crv) {
michael@0 171 /* if we're already logged in, we're good to go */
michael@0 172 case CKR_OK:
michael@0 173 slot->authTransact = PK11_Global.transaction;
michael@0 174 slot->authTime = currtime;
michael@0 175 rv = SECSuccess;
michael@0 176 break;
michael@0 177 case CKR_PIN_INCORRECT:
michael@0 178 PORT_SetError(SEC_ERROR_BAD_PASSWORD);
michael@0 179 rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
michael@0 180 break;
michael@0 181 default:
michael@0 182 PORT_SetError(PK11_MapError(crv));
michael@0 183 rv = SECFailure; /* some failure we can't fix by retrying */
michael@0 184 }
michael@0 185 return rv;
michael@0 186 }
michael@0 187
michael@0 188 SECStatus
michael@0 189 PK11_Logout(PK11SlotInfo *slot)
michael@0 190 {
michael@0 191 CK_RV crv;
michael@0 192
michael@0 193 /* force a logout */
michael@0 194 PK11_EnterSlotMonitor(slot);
michael@0 195 crv = PK11_GETTAB(slot)->C_Logout(slot->session);
michael@0 196 slot->lastLoginCheck = 0;
michael@0 197 PK11_ExitSlotMonitor(slot);
michael@0 198 if (crv != CKR_OK) {
michael@0 199 PORT_SetError(PK11_MapError(crv));
michael@0 200 return SECFailure;
michael@0 201 }
michael@0 202 return SECSuccess;
michael@0 203 }
michael@0 204
michael@0 205 /*
michael@0 206 * transaction stuff is for when we test for the need to do every
michael@0 207 * time auth to see if we already did it for this slot/transaction
michael@0 208 */
michael@0 209 void PK11_StartAuthTransaction(void)
michael@0 210 {
michael@0 211 PK11_Global.transaction++;
michael@0 212 PK11_Global.inTransaction = PR_TRUE;
michael@0 213 }
michael@0 214
michael@0 215 void PK11_EndAuthTransaction(void)
michael@0 216 {
michael@0 217 PK11_Global.transaction++;
michael@0 218 PK11_Global.inTransaction = PR_FALSE;
michael@0 219 }
michael@0 220
michael@0 221 /*
michael@0 222 * before we do a private key op, we check to see if we
michael@0 223 * need to reauthenticate.
michael@0 224 */
michael@0 225 void
michael@0 226 PK11_HandlePasswordCheck(PK11SlotInfo *slot,void *wincx)
michael@0 227 {
michael@0 228 int askpw = slot->askpw;
michael@0 229 PRBool NeedAuth = PR_FALSE;
michael@0 230
michael@0 231 if (!slot->needLogin) return;
michael@0 232
michael@0 233 if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
michael@0 234 PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
michael@0 235
michael@0 236 if (def_slot) {
michael@0 237 askpw = def_slot->askpw;
michael@0 238 PK11_FreeSlot(def_slot);
michael@0 239 }
michael@0 240 }
michael@0 241
michael@0 242 /* timeouts are handled by isLoggedIn */
michael@0 243 if (!PK11_IsLoggedIn(slot,wincx)) {
michael@0 244 NeedAuth = PR_TRUE;
michael@0 245 } else if (askpw == -1) {
michael@0 246 if (!PK11_Global.inTransaction ||
michael@0 247 (PK11_Global.transaction != slot->authTransact)) {
michael@0 248 PK11_EnterSlotMonitor(slot);
michael@0 249 PK11_GETTAB(slot)->C_Logout(slot->session);
michael@0 250 slot->lastLoginCheck = 0;
michael@0 251 PK11_ExitSlotMonitor(slot);
michael@0 252 NeedAuth = PR_TRUE;
michael@0 253 }
michael@0 254 }
michael@0 255 if (NeedAuth) PK11_DoPassword(slot, slot->session, PR_TRUE,
michael@0 256 wincx, PR_FALSE, PR_FALSE);
michael@0 257 }
michael@0 258
michael@0 259 void
michael@0 260 PK11_SlotDBUpdate(PK11SlotInfo *slot)
michael@0 261 {
michael@0 262 SECMOD_UpdateModule(slot->module);
michael@0 263 }
michael@0 264
michael@0 265 /*
michael@0 266 * set new askpw and timeout values
michael@0 267 */
michael@0 268 void
michael@0 269 PK11_SetSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout)
michael@0 270 {
michael@0 271 slot->askpw = askpw;
michael@0 272 slot->timeout = timeout;
michael@0 273 slot->defaultFlags |= PK11_OWN_PW_DEFAULTS;
michael@0 274 PK11_SlotDBUpdate(slot);
michael@0 275 }
michael@0 276
michael@0 277 /*
michael@0 278 * Get the askpw and timeout values for this slot
michael@0 279 */
michael@0 280 void
michael@0 281 PK11_GetSlotPWValues(PK11SlotInfo *slot,int *askpw, int *timeout)
michael@0 282 {
michael@0 283 *askpw = slot->askpw;
michael@0 284 *timeout = slot->timeout;
michael@0 285
michael@0 286 if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
michael@0 287 PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
michael@0 288
michael@0 289 if (def_slot) {
michael@0 290 *askpw = def_slot->askpw;
michael@0 291 *timeout = def_slot->timeout;
michael@0 292 PK11_FreeSlot(def_slot);
michael@0 293 }
michael@0 294 }
michael@0 295 }
michael@0 296
michael@0 297 /*
michael@0 298 * Returns true if the token is needLogin and isn't logged in.
michael@0 299 * This function is used to determine if authentication is needed
michael@0 300 * before attempting a potentially privelleged operation.
michael@0 301 */
michael@0 302 PRBool
michael@0 303 pk11_LoginStillRequired(PK11SlotInfo *slot, void *wincx)
michael@0 304 {
michael@0 305 return slot->needLogin && !PK11_IsLoggedIn(slot,wincx);
michael@0 306 }
michael@0 307
michael@0 308 /*
michael@0 309 * make sure a slot is authenticated...
michael@0 310 * This function only does the authentication if it is needed.
michael@0 311 */
michael@0 312 SECStatus
michael@0 313 PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) {
michael@0 314 if (pk11_LoginStillRequired(slot,wincx)) {
michael@0 315 return PK11_DoPassword(slot, slot->session, loadCerts, wincx,
michael@0 316 PR_FALSE, PR_FALSE);
michael@0 317 }
michael@0 318 return SECSuccess;
michael@0 319 }
michael@0 320
michael@0 321 /*
michael@0 322 * Authenticate to "unfriendly" tokens (tokens which need to be logged
michael@0 323 * in to find the certs.
michael@0 324 */
michael@0 325 SECStatus
michael@0 326 pk11_AuthenticateUnfriendly(PK11SlotInfo *slot, PRBool loadCerts, void *wincx)
michael@0 327 {
michael@0 328 SECStatus rv = SECSuccess;
michael@0 329 if (!PK11_IsFriendly(slot)) {
michael@0 330 rv = PK11_Authenticate(slot, loadCerts, wincx);
michael@0 331 }
michael@0 332 return rv;
michael@0 333 }
michael@0 334
michael@0 335
michael@0 336 /*
michael@0 337 * NOTE: this assumes that we are logged out of the card before hand
michael@0 338 */
michael@0 339 SECStatus
michael@0 340 PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw)
michael@0 341 {
michael@0 342 CK_SESSION_HANDLE rwsession;
michael@0 343 CK_RV crv;
michael@0 344 SECStatus rv = SECFailure;
michael@0 345 int len = 0;
michael@0 346
michael@0 347 /* get a rwsession */
michael@0 348 rwsession = PK11_GetRWSession(slot);
michael@0 349 if (rwsession == CK_INVALID_SESSION) {
michael@0 350 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 351 return rv;
michael@0 352 }
michael@0 353
michael@0 354 if (slot->protectedAuthPath) {
michael@0 355 len = 0;
michael@0 356 ssopw = NULL;
michael@0 357 } else if (ssopw == NULL) {
michael@0 358 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 359 return SECFailure;
michael@0 360 } else {
michael@0 361 len = PORT_Strlen(ssopw);
michael@0 362 }
michael@0 363
michael@0 364 /* check the password */
michael@0 365 crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO,
michael@0 366 (unsigned char *)ssopw,len);
michael@0 367 slot->lastLoginCheck = 0;
michael@0 368 switch (crv) {
michael@0 369 /* if we're already logged in, we're good to go */
michael@0 370 case CKR_OK:
michael@0 371 rv = SECSuccess;
michael@0 372 break;
michael@0 373 case CKR_PIN_INCORRECT:
michael@0 374 PORT_SetError(SEC_ERROR_BAD_PASSWORD);
michael@0 375 rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
michael@0 376 break;
michael@0 377 default:
michael@0 378 PORT_SetError(PK11_MapError(crv));
michael@0 379 rv = SECFailure; /* some failure we can't fix by retrying */
michael@0 380 }
michael@0 381 PK11_GETTAB(slot)->C_Logout(rwsession);
michael@0 382 slot->lastLoginCheck = 0;
michael@0 383
michael@0 384 /* release rwsession */
michael@0 385 PK11_RestoreROSession(slot,rwsession);
michael@0 386 return rv;
michael@0 387 }
michael@0 388
michael@0 389 /*
michael@0 390 * make sure the password conforms to your token's requirements.
michael@0 391 */
michael@0 392 SECStatus
michael@0 393 PK11_VerifyPW(PK11SlotInfo *slot,char *pw)
michael@0 394 {
michael@0 395 int len = PORT_Strlen(pw);
michael@0 396
michael@0 397 if ((slot->minPassword > len) || (slot->maxPassword < len)) {
michael@0 398 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 399 return SECFailure;
michael@0 400 }
michael@0 401 return SECSuccess;
michael@0 402 }
michael@0 403
michael@0 404 /*
michael@0 405 * initialize a user PIN Value
michael@0 406 */
michael@0 407 SECStatus
michael@0 408 PK11_InitPin(PK11SlotInfo *slot, const char *ssopw, const char *userpw)
michael@0 409 {
michael@0 410 CK_SESSION_HANDLE rwsession = CK_INVALID_SESSION;
michael@0 411 CK_RV crv;
michael@0 412 SECStatus rv = SECFailure;
michael@0 413 int len;
michael@0 414 int ssolen;
michael@0 415
michael@0 416 if (userpw == NULL) userpw = "";
michael@0 417 if (ssopw == NULL) ssopw = "";
michael@0 418
michael@0 419 len = PORT_Strlen(userpw);
michael@0 420 ssolen = PORT_Strlen(ssopw);
michael@0 421
michael@0 422 /* get a rwsession */
michael@0 423 rwsession = PK11_GetRWSession(slot);
michael@0 424 if (rwsession == CK_INVALID_SESSION) {
michael@0 425 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 426 slot->lastLoginCheck = 0;
michael@0 427 return rv;
michael@0 428 }
michael@0 429
michael@0 430 if (slot->protectedAuthPath) {
michael@0 431 len = 0;
michael@0 432 ssolen = 0;
michael@0 433 ssopw = NULL;
michael@0 434 userpw = NULL;
michael@0 435 }
michael@0 436
michael@0 437 /* check the password */
michael@0 438 crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO,
michael@0 439 (unsigned char *)ssopw,ssolen);
michael@0 440 slot->lastLoginCheck = 0;
michael@0 441 if (crv != CKR_OK) {
michael@0 442 PORT_SetError(PK11_MapError(crv));
michael@0 443 goto done;
michael@0 444 }
michael@0 445
michael@0 446 crv = PK11_GETTAB(slot)->C_InitPIN(rwsession,(unsigned char *)userpw,len);
michael@0 447 if (crv != CKR_OK) {
michael@0 448 PORT_SetError(PK11_MapError(crv));
michael@0 449 } else {
michael@0 450 rv = SECSuccess;
michael@0 451 }
michael@0 452
michael@0 453 done:
michael@0 454 PK11_GETTAB(slot)->C_Logout(rwsession);
michael@0 455 slot->lastLoginCheck = 0;
michael@0 456 PK11_RestoreROSession(slot,rwsession);
michael@0 457 if (rv == SECSuccess) {
michael@0 458 /* update our view of the world */
michael@0 459 PK11_InitToken(slot,PR_TRUE);
michael@0 460 if (slot->needLogin) {
michael@0 461 PK11_EnterSlotMonitor(slot);
michael@0 462 PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER,
michael@0 463 (unsigned char *)userpw,len);
michael@0 464 slot->lastLoginCheck = 0;
michael@0 465 PK11_ExitSlotMonitor(slot);
michael@0 466 }
michael@0 467 }
michael@0 468 return rv;
michael@0 469 }
michael@0 470
michael@0 471 /*
michael@0 472 * Change an existing user password
michael@0 473 */
michael@0 474 SECStatus
michael@0 475 PK11_ChangePW(PK11SlotInfo *slot, const char *oldpw, const char *newpw)
michael@0 476 {
michael@0 477 CK_RV crv;
michael@0 478 SECStatus rv = SECFailure;
michael@0 479 int newLen = 0;
michael@0 480 int oldLen = 0;
michael@0 481 CK_SESSION_HANDLE rwsession;
michael@0 482
michael@0 483 /* use NULL values to trigger the protected authentication path */
michael@0 484 if (!slot->protectedAuthPath) {
michael@0 485 if (newpw == NULL) newpw = "";
michael@0 486 if (oldpw == NULL) oldpw = "";
michael@0 487 }
michael@0 488 if (newpw) newLen = PORT_Strlen(newpw);
michael@0 489 if (oldpw) oldLen = PORT_Strlen(oldpw);
michael@0 490
michael@0 491 /* get a rwsession */
michael@0 492 rwsession = PK11_GetRWSession(slot);
michael@0 493 if (rwsession == CK_INVALID_SESSION) {
michael@0 494 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 495 return rv;
michael@0 496 }
michael@0 497
michael@0 498 crv = PK11_GETTAB(slot)->C_SetPIN(rwsession,
michael@0 499 (unsigned char *)oldpw,oldLen,(unsigned char *)newpw,newLen);
michael@0 500 if (crv == CKR_OK) {
michael@0 501 rv = SECSuccess;
michael@0 502 } else {
michael@0 503 PORT_SetError(PK11_MapError(crv));
michael@0 504 }
michael@0 505
michael@0 506 PK11_RestoreROSession(slot,rwsession);
michael@0 507
michael@0 508 /* update our view of the world */
michael@0 509 PK11_InitToken(slot,PR_TRUE);
michael@0 510 return rv;
michael@0 511 }
michael@0 512
michael@0 513 static char *
michael@0 514 pk11_GetPassword(PK11SlotInfo *slot, PRBool retry, void * wincx)
michael@0 515 {
michael@0 516 if (PK11_Global.getPass == NULL) return NULL;
michael@0 517 return (*PK11_Global.getPass)(slot, retry, wincx);
michael@0 518 }
michael@0 519
michael@0 520 void
michael@0 521 PK11_SetPasswordFunc(PK11PasswordFunc func)
michael@0 522 {
michael@0 523 PK11_Global.getPass = func;
michael@0 524 }
michael@0 525
michael@0 526 void
michael@0 527 PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func)
michael@0 528 {
michael@0 529 PK11_Global.verifyPass = func;
michael@0 530 }
michael@0 531
michael@0 532 void
michael@0 533 PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func)
michael@0 534 {
michael@0 535 PK11_Global.isLoggedIn = func;
michael@0 536 }
michael@0 537
michael@0 538
michael@0 539 /*
michael@0 540 * authenticate to a slot. This loops until we can't recover, the user
michael@0 541 * gives up, or we succeed. If we're already logged in and this function
michael@0 542 * is called we will still prompt for a password, but we will probably
michael@0 543 * succeed no matter what the password was (depending on the implementation
michael@0 544 * of the PKCS 11 module.
michael@0 545 */
michael@0 546 SECStatus
michael@0 547 PK11_DoPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
michael@0 548 PRBool loadCerts, void *wincx, PRBool alreadyLocked,
michael@0 549 PRBool contextSpecific)
michael@0 550 {
michael@0 551 SECStatus rv = SECFailure;
michael@0 552 char * password;
michael@0 553 PRBool attempt = PR_FALSE;
michael@0 554
michael@0 555 if (PK11_NeedUserInit(slot)) {
michael@0 556 PORT_SetError(SEC_ERROR_IO);
michael@0 557 return SECFailure;
michael@0 558 }
michael@0 559
michael@0 560
michael@0 561 /*
michael@0 562 * Central server type applications which control access to multiple
michael@0 563 * slave applications to single crypto devices need to virtuallize the
michael@0 564 * login state. This is done by a callback out of PK11_IsLoggedIn and
michael@0 565 * here. If we are actually logged in, then we got here because the
michael@0 566 * higher level code told us that the particular client application may
michael@0 567 * still need to be logged in. If that is the case, we simply tell the
michael@0 568 * server code that it should now verify the clients password and tell us
michael@0 569 * the results.
michael@0 570 */
michael@0 571 if (PK11_IsLoggedIn(slot,NULL) &&
michael@0 572 (PK11_Global.verifyPass != NULL)) {
michael@0 573 if (!PK11_Global.verifyPass(slot,wincx)) {
michael@0 574 PORT_SetError(SEC_ERROR_BAD_PASSWORD);
michael@0 575 return SECFailure;
michael@0 576 }
michael@0 577 return SECSuccess;
michael@0 578 }
michael@0 579
michael@0 580 /* get the password. This can drop out of the while loop
michael@0 581 * for the following reasons:
michael@0 582 * (1) the user refused to enter a password.
michael@0 583 * (return error to caller)
michael@0 584 * (2) the token user password is disabled [usually due to
michael@0 585 * too many failed authentication attempts].
michael@0 586 * (return error to caller)
michael@0 587 * (3) the password was successful.
michael@0 588 */
michael@0 589 while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) {
michael@0 590 /* if the token has a protectedAuthPath, the application may have
michael@0 591 * already issued the C_Login as part of it's pk11_GetPassword call.
michael@0 592 * In this case the application will tell us what the results were in
michael@0 593 * the password value (retry or the authentication was successful) so
michael@0 594 * we can skip our own C_Login call (which would force the token to
michael@0 595 * try to login again).
michael@0 596 *
michael@0 597 * Applications that don't know about protectedAuthPath will return a
michael@0 598 * password, which we will ignore and trigger the token to
michael@0 599 * 'authenticate' itself anyway. Hopefully the blinking display on
michael@0 600 * the reader, or the flashing light under the thumbprint reader will
michael@0 601 * attract the user's attention */
michael@0 602 attempt = PR_TRUE;
michael@0 603 if (slot->protectedAuthPath) {
michael@0 604 /* application tried to authenticate and failed. it wants to try
michael@0 605 * again, continue looping */
michael@0 606 if (strcmp(password, PK11_PW_RETRY) == 0) {
michael@0 607 rv = SECWouldBlock;
michael@0 608 PORT_Free(password);
michael@0 609 continue;
michael@0 610 }
michael@0 611 /* applicaton tried to authenticate and succeeded we're done */
michael@0 612 if (strcmp(password, PK11_PW_AUTHENTICATED) == 0) {
michael@0 613 rv = SECSuccess;
michael@0 614 PORT_Free(password);
michael@0 615 break;
michael@0 616 }
michael@0 617 }
michael@0 618 rv = pk11_CheckPassword(slot, session, password,
michael@0 619 alreadyLocked, contextSpecific);
michael@0 620 PORT_Memset(password, 0, PORT_Strlen(password));
michael@0 621 PORT_Free(password);
michael@0 622 if (rv != SECWouldBlock) break;
michael@0 623 }
michael@0 624 if (rv == SECSuccess) {
michael@0 625 if (!PK11_IsFriendly(slot)) {
michael@0 626 nssTrustDomain_UpdateCachedTokenCerts(slot->nssToken->trustDomain,
michael@0 627 slot->nssToken);
michael@0 628 }
michael@0 629 } else if (!attempt) PORT_SetError(SEC_ERROR_BAD_PASSWORD);
michael@0 630 return rv;
michael@0 631 }
michael@0 632
michael@0 633 void PK11_LogoutAll(void)
michael@0 634 {
michael@0 635 SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
michael@0 636 SECMODModuleList *modList;
michael@0 637 SECMODModuleList *mlp = NULL;
michael@0 638 int i;
michael@0 639
michael@0 640 /* NSS is not initialized, there are not tokens to log out */
michael@0 641 if (lock == NULL) {
michael@0 642 return;
michael@0 643 }
michael@0 644
michael@0 645 SECMOD_GetReadLock(lock);
michael@0 646 modList = SECMOD_GetDefaultModuleList();
michael@0 647 /* find the number of entries */
michael@0 648 for (mlp = modList; mlp != NULL; mlp = mlp->next) {
michael@0 649 for (i=0; i < mlp->module->slotCount; i++) {
michael@0 650 PK11_Logout(mlp->module->slots[i]);
michael@0 651 }
michael@0 652 }
michael@0 653
michael@0 654 SECMOD_ReleaseReadLock(lock);
michael@0 655 }
michael@0 656
michael@0 657 int
michael@0 658 PK11_GetMinimumPwdLength(PK11SlotInfo *slot)
michael@0 659 {
michael@0 660 return ((int)slot->minPassword);
michael@0 661 }
michael@0 662
michael@0 663 /* Does this slot have a protected pin path? */
michael@0 664 PRBool
michael@0 665 PK11_ProtectedAuthenticationPath(PK11SlotInfo *slot)
michael@0 666 {
michael@0 667 return slot->protectedAuthPath;
michael@0 668 }
michael@0 669
michael@0 670 /*
michael@0 671 * we can initialize the password if 1) The toke is not inited
michael@0 672 * (need login == true and see need UserInit) or 2) the token has
michael@0 673 * a NULL password. (slot->needLogin = false & need user Init = false).
michael@0 674 */
michael@0 675 PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot)
michael@0 676 {
michael@0 677 if (slot->needLogin && PK11_NeedUserInit(slot)) {
michael@0 678 return PR_TRUE;
michael@0 679 }
michael@0 680 if (!slot->needLogin && !PK11_NeedUserInit(slot)) {
michael@0 681 return PR_TRUE;
michael@0 682 }
michael@0 683 return PR_FALSE;
michael@0 684 }
michael@0 685
michael@0 686 PRBool PK11_NeedPWInit()
michael@0 687 {
michael@0 688 PK11SlotInfo *slot = PK11_GetInternalKeySlot();
michael@0 689 PRBool ret = PK11_NeedPWInitForSlot(slot);
michael@0 690
michael@0 691 PK11_FreeSlot(slot);
michael@0 692 return ret;
michael@0 693 }
michael@0 694
michael@0 695 PRBool
michael@0 696 pk11_InDelayPeriod(PRIntervalTime lastTime, PRIntervalTime delayTime,
michael@0 697 PRIntervalTime *retTime)
michael@0 698 {
michael@0 699 PRIntervalTime time;
michael@0 700
michael@0 701 *retTime = time = PR_IntervalNow();
michael@0 702 return (PRBool) (lastTime) && ((time-lastTime) < delayTime);
michael@0 703 }
michael@0 704
michael@0 705 /*
michael@0 706 * Determine if the token is logged in. We have to actually query the token,
michael@0 707 * because it's state can change without intervention from us.
michael@0 708 */
michael@0 709 PRBool
michael@0 710 PK11_IsLoggedIn(PK11SlotInfo *slot,void *wincx)
michael@0 711 {
michael@0 712 CK_SESSION_INFO sessionInfo;
michael@0 713 int askpw = slot->askpw;
michael@0 714 int timeout = slot->timeout;
michael@0 715 CK_RV crv;
michael@0 716 PRIntervalTime curTime;
michael@0 717 static PRIntervalTime login_delay_time = 0;
michael@0 718
michael@0 719 if (login_delay_time == 0) {
michael@0 720 login_delay_time = PR_SecondsToInterval(1);
michael@0 721 }
michael@0 722
michael@0 723 /* If we don't have our own password default values, use the system
michael@0 724 * ones */
michael@0 725 if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
michael@0 726 PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
michael@0 727
michael@0 728 if (def_slot) {
michael@0 729 askpw = def_slot->askpw;
michael@0 730 timeout = def_slot->timeout;
michael@0 731 PK11_FreeSlot(def_slot);
michael@0 732 }
michael@0 733 }
michael@0 734
michael@0 735 if ((wincx != NULL) && (PK11_Global.isLoggedIn != NULL) &&
michael@0 736 (*PK11_Global.isLoggedIn)(slot, wincx) == PR_FALSE) { return PR_FALSE; }
michael@0 737
michael@0 738
michael@0 739 /* forget the password if we've been inactive too long */
michael@0 740 if (askpw == 1) {
michael@0 741 PRTime currtime = PR_Now();
michael@0 742 PRTime result;
michael@0 743 PRTime mult;
michael@0 744
michael@0 745 LL_I2L(result, timeout);
michael@0 746 LL_I2L(mult, 60*1000*1000);
michael@0 747 LL_MUL(result,result,mult);
michael@0 748 LL_ADD(result, result, slot->authTime);
michael@0 749 if (LL_CMP(result, <, currtime) ) {
michael@0 750 PK11_EnterSlotMonitor(slot);
michael@0 751 PK11_GETTAB(slot)->C_Logout(slot->session);
michael@0 752 slot->lastLoginCheck = 0;
michael@0 753 PK11_ExitSlotMonitor(slot);
michael@0 754 } else {
michael@0 755 slot->authTime = currtime;
michael@0 756 }
michael@0 757 }
michael@0 758
michael@0 759 PK11_EnterSlotMonitor(slot);
michael@0 760 if (pk11_InDelayPeriod(slot->lastLoginCheck,login_delay_time, &curTime)) {
michael@0 761 sessionInfo.state = slot->lastState;
michael@0 762 crv = CKR_OK;
michael@0 763 } else {
michael@0 764 crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session,&sessionInfo);
michael@0 765 if (crv == CKR_OK) {
michael@0 766 slot->lastState = sessionInfo.state;
michael@0 767 slot->lastLoginCheck = curTime;
michael@0 768 }
michael@0 769 }
michael@0 770 PK11_ExitSlotMonitor(slot);
michael@0 771 /* if we can't get session info, something is really wrong */
michael@0 772 if (crv != CKR_OK) {
michael@0 773 slot->session = CK_INVALID_SESSION;
michael@0 774 return PR_FALSE;
michael@0 775 }
michael@0 776
michael@0 777 switch (sessionInfo.state) {
michael@0 778 case CKS_RW_PUBLIC_SESSION:
michael@0 779 case CKS_RO_PUBLIC_SESSION:
michael@0 780 default:
michael@0 781 break; /* fail */
michael@0 782 case CKS_RW_USER_FUNCTIONS:
michael@0 783 case CKS_RW_SO_FUNCTIONS:
michael@0 784 case CKS_RO_USER_FUNCTIONS:
michael@0 785 return PR_TRUE;
michael@0 786 }
michael@0 787 return PR_FALSE;
michael@0 788 }

mercurial