PageRenderTime 146ms CodeModel.GetById 13ms app.highlight 120ms RepoModel.GetById 1ms app.codeStats 0ms

/security/nss/lib/softoken/legacydb/keydb.c

http://github.com/zpao/v8monkey
C | 2272 lines | 1657 code | 370 blank | 245 comment | 415 complexity | 09025043ef2509d9863423aad34b7427 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* ***** BEGIN LICENSE BLOCK *****
   2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   3 *
   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/
   8 *
   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
  12 * License.
  13 *
  14 * The Original Code is the Netscape security libraries.
  15 *
  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.
  20 *
  21 * Contributor(s):
  22 *   Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
  23 *
  24 * Alternatively, the contents of this file may be used under the terms of
  25 * either the GNU General Public License Version 2 or later (the "GPL"), or
  26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27 * in which case the provisions of the GPL or the LGPL are applicable instead
  28 * of those above. If you wish to allow use of your version of this file only
  29 * under the terms of either the GPL or the LGPL, and not to allow others to
  30 * use your version of this file under the terms of the MPL, indicate your
  31 * decision by deleting the provisions above and replace them with the notice
  32 * and other provisions required by the GPL or the LGPL. If you do not delete
  33 * the provisions above, a recipient may use your version of this file under
  34 * the terms of any one of the MPL, the GPL or the LGPL.
  35 *
  36 * ***** END LICENSE BLOCK ***** */
  37/* $Id: keydb.c,v 1.12 2010/07/20 01:26:04 wtc%google.com Exp $ */
  38
  39#include "lowkeyi.h"
  40#include "secasn1.h"
  41#include "secder.h"
  42#include "secoid.h"
  43#include "blapi.h"
  44#include "secitem.h"
  45#include "pcert.h"
  46#include "mcom_db.h"
  47#include "secerr.h"
  48
  49#include "keydbi.h"
  50#include "lgdb.h"
  51
  52/*
  53 * Record keys for keydb
  54 */
  55#define SALT_STRING "global-salt"
  56#define VERSION_STRING "Version"
  57#define KEYDB_PW_CHECK_STRING	"password-check"
  58#define KEYDB_PW_CHECK_LEN	14
  59#define KEYDB_FAKE_PW_CHECK_STRING	"fake-password-check"
  60#define KEYDB_FAKE_PW_CHECK_LEN	19
  61
  62/* Size of the global salt for key database */
  63#define SALT_LENGTH     16
  64
  65SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
  66
  67const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[] = {
  68    { SEC_ASN1_SEQUENCE,
  69	0, NULL, sizeof(NSSLOWKEYEncryptedPrivateKeyInfo) },
  70    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
  71	offsetof(NSSLOWKEYEncryptedPrivateKeyInfo,algorithm),
  72	SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
  73    { SEC_ASN1_OCTET_STRING,
  74	offsetof(NSSLOWKEYEncryptedPrivateKeyInfo,encryptedData) },
  75    { 0 }
  76};
  77
  78const SEC_ASN1Template nsslowkey_PointerToEncryptedPrivateKeyInfoTemplate[] = {
  79	{ SEC_ASN1_POINTER, 0, nsslowkey_EncryptedPrivateKeyInfoTemplate }
  80};
  81
  82
  83/* ====== Default key databse encryption algorithm ====== */
  84static void
  85sec_destroy_dbkey(NSSLOWKEYDBKey *dbkey)
  86{
  87    if ( dbkey && dbkey->arena ) {
  88	PORT_FreeArena(dbkey->arena, PR_FALSE);
  89    }
  90}
  91
  92static void
  93free_dbt(DBT *dbt)
  94{
  95    if ( dbt ) {
  96	PORT_Free(dbt->data);
  97	PORT_Free(dbt);
  98    }
  99    
 100    return;
 101}
 102
 103static int keydb_Get(NSSLOWKEYDBHandle *db, DBT *key, DBT *data, 
 104		     unsigned int flags);
 105static int keydb_Put(NSSLOWKEYDBHandle *db, DBT *key, DBT *data, 
 106		     unsigned int flags);
 107static int keydb_Sync(NSSLOWKEYDBHandle *db, unsigned int flags);
 108static int keydb_Del(NSSLOWKEYDBHandle *db, DBT *key, unsigned int flags);
 109static int keydb_Seq(NSSLOWKEYDBHandle *db, DBT *key, DBT *data, 
 110		     unsigned int flags);
 111static void keydb_Close(NSSLOWKEYDBHandle *db);
 112
 113/*
 114 * format of key database entries for version 3 of database:
 115 *	byte offset	field
 116 *	-----------	-----
 117 *	0		version
 118 *	1		salt-len
 119 *	2		nn-len
 120 *	3..		salt-data
 121 *	...		nickname
 122 *	...		encrypted-key-data
 123 */
 124static DBT *
 125encode_dbkey(NSSLOWKEYDBKey *dbkey,unsigned char version)
 126{
 127    DBT *bufitem = NULL;
 128    unsigned char *buf;
 129    int nnlen;
 130    char *nn;
 131    
 132    bufitem = (DBT *)PORT_ZAlloc(sizeof(DBT));
 133    if ( bufitem == NULL ) {
 134	goto loser;
 135    }
 136    
 137    if ( dbkey->nickname ) {
 138	nn = dbkey->nickname;
 139	nnlen = PORT_Strlen(nn) + 1;
 140    } else {
 141	nn = "";
 142	nnlen = 1;
 143    }
 144    
 145    /* compute the length of the record */
 146    /* 1 + 1 + 1 == version number header + salt length + nn len */
 147    bufitem->size = dbkey->salt.len + nnlen + dbkey->derPK.len + 1 + 1 + 1;
 148    
 149    bufitem->data = (void *)PORT_ZAlloc(bufitem->size);
 150    if ( bufitem->data == NULL ) {
 151	goto loser;
 152    }
 153
 154    buf = (unsigned char *)bufitem->data;
 155    
 156    /* set version number */
 157    buf[0] = version;
 158
 159    /* set length of salt */
 160    PORT_Assert(dbkey->salt.len < 256);
 161    buf[1] = dbkey->salt.len;
 162
 163    /* set length of nickname */
 164    PORT_Assert(nnlen < 256);
 165    buf[2] = nnlen;
 166
 167    /* copy salt */
 168    PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len);
 169
 170    /* copy nickname */
 171    PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen);
 172
 173    /* copy encrypted key */
 174    PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data,
 175	      dbkey->derPK.len);
 176    
 177    return(bufitem);
 178    
 179loser:
 180    if ( bufitem ) {
 181	free_dbt(bufitem);
 182    }
 183    
 184    return(NULL);
 185}
 186
 187static NSSLOWKEYDBKey *
 188decode_dbkey(DBT *bufitem, int expectedVersion)
 189{
 190    NSSLOWKEYDBKey *dbkey;
 191    PLArenaPool *arena = NULL;
 192    unsigned char *buf;
 193    int version;
 194    int keyoff;
 195    int nnlen;
 196    int saltoff;
 197    
 198    buf = (unsigned char *)bufitem->data;
 199
 200    version = buf[0];
 201    
 202    if ( version != expectedVersion ) {
 203	goto loser;
 204    }
 205    
 206    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
 207    if ( arena == NULL ) {
 208	goto loser;
 209    }
 210    
 211    dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
 212    if ( dbkey == NULL ) {
 213	goto loser;
 214    }
 215
 216    dbkey->arena = arena;
 217    dbkey->salt.data = NULL;
 218    dbkey->derPK.data = NULL;
 219    
 220    dbkey->salt.len = buf[1];
 221    dbkey->salt.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->salt.len);
 222    if ( dbkey->salt.data == NULL ) {
 223	goto loser;
 224    }
 225
 226    saltoff = 2;
 227    keyoff = 2 + dbkey->salt.len;
 228    
 229    if ( expectedVersion >= 3 ) {
 230	nnlen = buf[2];
 231	if ( nnlen ) {
 232	    dbkey->nickname = (char *)PORT_ArenaZAlloc(arena, nnlen + 1);
 233	    if ( dbkey->nickname ) {
 234		PORT_Memcpy(dbkey->nickname, &buf[keyoff+1], nnlen);
 235	    }
 236	}
 237	keyoff += ( nnlen + 1 );
 238	saltoff = 3;
 239    }
 240
 241    PORT_Memcpy(dbkey->salt.data, &buf[saltoff], dbkey->salt.len);
 242    
 243    dbkey->derPK.len = bufitem->size - keyoff;
 244    dbkey->derPK.data = (unsigned char *)PORT_ArenaZAlloc(arena,dbkey->derPK.len);
 245    if ( dbkey->derPK.data == NULL ) {
 246	goto loser;
 247    }
 248    
 249    PORT_Memcpy(dbkey->derPK.data, &buf[keyoff], dbkey->derPK.len);
 250    
 251    return(dbkey);
 252    
 253loser:
 254
 255    if ( arena ) {
 256	PORT_FreeArena(arena, PR_FALSE);
 257    }
 258    
 259    return(NULL);
 260}
 261
 262static NSSLOWKEYDBKey *
 263get_dbkey(NSSLOWKEYDBHandle *handle, DBT *index)
 264{
 265    NSSLOWKEYDBKey *dbkey;
 266    DBT entry;
 267    int ret;
 268    
 269    /* get it from the database */
 270    ret = keydb_Get(handle, index, &entry, 0);
 271    if ( ret ) {
 272	PORT_SetError(SEC_ERROR_BAD_DATABASE);
 273	return NULL;
 274    }
 275
 276    /* set up dbkey struct */
 277
 278    dbkey = decode_dbkey(&entry, handle->version);
 279
 280    return(dbkey);
 281}
 282
 283static SECStatus
 284put_dbkey(NSSLOWKEYDBHandle *handle, DBT *index, NSSLOWKEYDBKey *dbkey, PRBool update)
 285{
 286    DBT *keydata = NULL;
 287    int status;
 288    
 289    keydata = encode_dbkey(dbkey, handle->version);
 290    if ( keydata == NULL ) {
 291	goto loser;
 292    }
 293    
 294    /* put it in the database */
 295    if ( update ) {
 296	status = keydb_Put(handle, index, keydata, 0);
 297    } else {
 298	status = keydb_Put(handle, index, keydata, R_NOOVERWRITE);
 299    }
 300    
 301    if ( status ) {
 302	goto loser;
 303    }
 304
 305    /* sync the database */
 306    status = keydb_Sync(handle, 0);
 307    if ( status ) {
 308	goto loser;
 309    }
 310
 311    free_dbt(keydata);
 312    return(SECSuccess);
 313
 314loser:
 315    if ( keydata ) {
 316	free_dbt(keydata);
 317    }
 318    
 319    return(SECFailure);
 320}
 321
 322SECStatus
 323nsslowkey_TraverseKeys(NSSLOWKEYDBHandle *handle, 
 324		 SECStatus (* keyfunc)(DBT *k, DBT *d, void *pdata),
 325		 void *udata )
 326{
 327    DBT data;
 328    DBT key;
 329    SECStatus status;
 330    int ret;
 331
 332    if (handle == NULL) {
 333	return(SECFailure);
 334    }
 335
 336    ret = keydb_Seq(handle, &key, &data, R_FIRST);
 337    if ( ret ) {
 338	return(SECFailure);
 339    }
 340    
 341    do {
 342	/* skip version record */
 343	if ( data.size > 1 ) {
 344	    if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
 345		if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
 346		    continue;
 347		}
 348	    }
 349
 350	    /* skip password check */
 351	    if ( key.size == KEYDB_PW_CHECK_LEN ) {
 352		if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
 353				 KEYDB_PW_CHECK_LEN) == 0 ) {
 354		    continue;
 355		}
 356	    }
 357	    
 358	    status = (* keyfunc)(&key, &data, udata);
 359	    if (status != SECSuccess) {
 360		return(status);
 361	    }
 362	}
 363    } while ( keydb_Seq(handle, &key, &data, R_NEXT) == 0 );
 364
 365    return(SECSuccess);
 366}
 367
 368#ifdef notdef
 369typedef struct keyNode {
 370    struct keyNode *next;
 371    DBT key;
 372} keyNode;
 373
 374typedef struct {
 375    PLArenaPool *arena;
 376    keyNode *head;
 377} keyList;
 378
 379static SECStatus
 380sec_add_key_to_list(DBT *key, DBT *data, void *arg)
 381{
 382    keyList *keylist;
 383    keyNode *node;
 384    void *keydata;
 385    
 386    keylist = (keyList *)arg;
 387
 388    /* allocate the node struct */
 389    node = (keyNode*)PORT_ArenaZAlloc(keylist->arena, sizeof(keyNode));
 390    if ( node == NULL ) {
 391	return(SECFailure);
 392    }
 393    
 394    /* allocate room for key data */
 395    keydata = PORT_ArenaZAlloc(keylist->arena, key->size);
 396    if ( keydata == NULL ) {
 397	return(SECFailure);
 398    }
 399
 400    /* link node into list */
 401    node->next = keylist->head;
 402    keylist->head = node;
 403
 404    /* copy key into node */
 405    PORT_Memcpy(keydata, key->data, key->size);
 406    node->key.size = key->size;
 407    node->key.data = keydata;
 408    
 409    return(SECSuccess);
 410}
 411#endif
 412
 413static SECItem *
 414decodeKeyDBGlobalSalt(DBT *saltData)
 415{
 416    SECItem *saltitem;
 417    
 418    saltitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
 419    if ( saltitem == NULL ) {
 420	return(NULL);
 421    }
 422    
 423    saltitem->data = (unsigned char *)PORT_ZAlloc(saltData->size);
 424    if ( saltitem->data == NULL ) {
 425	PORT_Free(saltitem);
 426	return(NULL);
 427    }
 428    
 429    saltitem->len = saltData->size;
 430    PORT_Memcpy(saltitem->data, saltData->data, saltitem->len);
 431    
 432    return(saltitem);
 433}
 434
 435static SECItem *
 436GetKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle)
 437{
 438    DBT saltKey;
 439    DBT saltData;
 440    int ret;
 441    
 442    saltKey.data = SALT_STRING;
 443    saltKey.size = sizeof(SALT_STRING) - 1;
 444
 445    ret = keydb_Get(handle, &saltKey, &saltData, 0);
 446    if ( ret ) {
 447	return(NULL);
 448    }
 449
 450    return(decodeKeyDBGlobalSalt(&saltData));
 451}
 452
 453static SECStatus
 454StoreKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle, SECItem *salt)
 455{
 456    DBT saltKey;
 457    DBT saltData;
 458    int status;
 459    
 460    saltKey.data = SALT_STRING;
 461    saltKey.size = sizeof(SALT_STRING) - 1;
 462
 463    saltData.data = (void *)salt->data;
 464    saltData.size = salt->len;
 465
 466    /* put global salt into the database now */
 467    status = keydb_Put(handle, &saltKey, &saltData, 0);
 468    if ( status ) {
 469	return(SECFailure);
 470    }
 471
 472    return(SECSuccess);
 473}
 474
 475static SECStatus
 476makeGlobalVersion(NSSLOWKEYDBHandle *handle)
 477{
 478    unsigned char version;
 479    DBT versionData;
 480    DBT versionKey;
 481    int status;
 482    
 483    version = NSSLOWKEY_DB_FILE_VERSION;
 484    versionData.data = &version;
 485    versionData.size = 1;
 486    versionKey.data = VERSION_STRING;
 487    versionKey.size = sizeof(VERSION_STRING)-1;
 488		
 489    /* put version string into the database now */
 490    status = keydb_Put(handle, &versionKey, &versionData, 0);
 491    if ( status ) {
 492	return(SECFailure);
 493    }
 494    handle->version = version;
 495
 496    return(SECSuccess);
 497}
 498
 499
 500static SECStatus
 501makeGlobalSalt(NSSLOWKEYDBHandle *handle)
 502{
 503    DBT saltKey;
 504    DBT saltData;
 505    unsigned char saltbuf[16];
 506    int status;
 507    
 508    saltKey.data = SALT_STRING;
 509    saltKey.size = sizeof(SALT_STRING) - 1;
 510
 511    saltData.data = (void *)saltbuf;
 512    saltData.size = sizeof(saltbuf);
 513    RNG_GenerateGlobalRandomBytes(saltbuf, sizeof(saltbuf));
 514
 515    /* put global salt into the database now */
 516    status = keydb_Put(handle, &saltKey, &saltData, 0);
 517    if ( status ) {
 518	return(SECFailure);
 519    }
 520
 521    return(SECSuccess);
 522}
 523
 524static SECStatus
 525encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
 526		   SECItem *encCheck);
 527
 528static unsigned char
 529nsslowkey_version(NSSLOWKEYDBHandle *handle)
 530{
 531    DBT versionKey;
 532    DBT versionData;
 533    int ret;
 534    versionKey.data = VERSION_STRING;
 535    versionKey.size = sizeof(VERSION_STRING)-1;
 536
 537    if (handle->db == NULL) {
 538	return 255;
 539    }
 540
 541    /* lookup version string in database */
 542    ret = keydb_Get( handle, &versionKey, &versionData, 0 );
 543
 544    /* error accessing the database */
 545    if ( ret < 0 ) {
 546	return 255;
 547    }
 548
 549    if ( ret >= 1 ) {
 550	return 0;
 551    }
 552    return *( (unsigned char *)versionData.data);
 553}
 554
 555static PRBool
 556seckey_HasAServerKey(NSSLOWKEYDBHandle *handle)
 557{
 558    DBT key;
 559    DBT data;
 560    int ret;
 561    PRBool found = PR_FALSE;
 562
 563    ret = keydb_Seq(handle, &key, &data, R_FIRST);
 564    if ( ret ) {
 565	return PR_FALSE;
 566    }
 567    
 568    do {
 569	/* skip version record */
 570	if ( data.size > 1 ) {
 571	    /* skip salt */
 572	    if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
 573		if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
 574		    continue;
 575		}
 576	    }
 577	    /* skip pw check entry */
 578	    if ( key.size == KEYDB_PW_CHECK_LEN ) {
 579		if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING, 
 580						KEYDB_PW_CHECK_LEN) == 0 ) {
 581		    continue;
 582		}
 583	    }
 584
 585	    /* keys stored by nickname will have 0 as the last byte of the
 586	     * db key.  Other keys must be stored by modulus.  We will not
 587	     * update those because they are left over from a keygen that
 588	     * never resulted in a cert.
 589	     */
 590	    if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
 591		continue;
 592	    }
 593
 594	    if (PORT_Strcmp(key.data,"Server-Key") == 0) {
 595		found = PR_TRUE;
 596	        break;
 597	    }
 598	    
 599	}
 600    } while ( keydb_Seq(handle, &key, &data, R_NEXT) == 0 );
 601
 602    return found;
 603}
 604
 605/* forward declare local create function */
 606static NSSLOWKEYDBHandle * nsslowkey_NewHandle(DB *dbHandle);
 607
 608/*
 609 * currently updates key database from v2 to v3
 610 */
 611static SECStatus
 612nsslowkey_UpdateKeyDBPass1(NSSLOWKEYDBHandle *handle)
 613{
 614    SECStatus rv;
 615    DBT checkKey;
 616    DBT checkData;
 617    DBT saltKey;
 618    DBT saltData;
 619    DBT key;
 620    DBT data;
 621    unsigned char version;
 622    NSSLOWKEYDBKey *dbkey = NULL;
 623    NSSLOWKEYDBHandle *update = NULL;
 624    SECItem *oldSalt = NULL;
 625    int ret;
 626    SECItem checkitem;
 627
 628    if ( handle->updatedb == NULL ) {
 629	return SECSuccess;
 630    }
 631
 632    /* create a full DB Handle for our update so we 
 633     * can use the correct locks for the db primatives */
 634    update = nsslowkey_NewHandle(handle->updatedb);
 635    if ( update == NULL) {
 636	return SECSuccess;
 637    }
 638
 639    /* update has now inherited the database handle */
 640    handle->updatedb = NULL;
 641
 642    /*
 643     * check the version record
 644     */
 645    version = nsslowkey_version(update);
 646    if (version != 2) {
 647	goto done;
 648    }
 649
 650    saltKey.data = SALT_STRING;
 651    saltKey.size = sizeof(SALT_STRING) - 1;
 652
 653    ret = keydb_Get(update, &saltKey, &saltData, 0);
 654    if ( ret ) {
 655	/* no salt in old db, so it is corrupted */
 656	goto done;
 657    }
 658
 659    oldSalt = decodeKeyDBGlobalSalt(&saltData);
 660    if ( oldSalt == NULL ) {
 661	/* bad salt in old db, so it is corrupted */
 662	goto done;
 663    }
 664
 665    /*
 666     * look for a pw check entry
 667     */
 668    checkKey.data = KEYDB_PW_CHECK_STRING;
 669    checkKey.size = KEYDB_PW_CHECK_LEN;
 670    
 671    ret = keydb_Get(update, &checkKey, &checkData, 0 );
 672    if (ret) {
 673	/*
 674	 * if we have a key, but no KEYDB_PW_CHECK_STRING, then this must
 675	 * be an old server database, and it does have a password associated
 676	 * with it. Put a fake entry in so we can identify this db when we do
 677	 * get the password for it.
 678	 */
 679	if (seckey_HasAServerKey(update)) {
 680	    DBT fcheckKey;
 681	    DBT fcheckData;
 682
 683	    /*
 684	     * include a fake string
 685	     */
 686	    fcheckKey.data = KEYDB_FAKE_PW_CHECK_STRING;
 687	    fcheckKey.size = KEYDB_FAKE_PW_CHECK_LEN;
 688	    fcheckData.data = "1";
 689	    fcheckData.size = 1;
 690	    /* put global salt into the new database now */
 691	    ret = keydb_Put( handle, &saltKey, &saltData, 0);
 692	    if ( ret ) {
 693		goto done;
 694	    }
 695	    ret = keydb_Put( handle, &fcheckKey, &fcheckData, 0);
 696	    if ( ret ) {
 697		goto done;
 698	    }
 699	} else {
 700	    goto done;
 701	}
 702    } else {
 703	/* put global salt into the new database now */
 704	ret = keydb_Put( handle, &saltKey, &saltData, 0);
 705	if ( ret ) {
 706	    goto done;
 707	}
 708
 709	dbkey = decode_dbkey(&checkData, 2);
 710	if ( dbkey == NULL ) {
 711	    goto done;
 712	}
 713	checkitem = dbkey->derPK;
 714	dbkey->derPK.data = NULL;
 715    
 716	/* format the new pw check entry */
 717	rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem);
 718	if ( rv != SECSuccess ) {
 719	    goto done;
 720	}
 721
 722	rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE);
 723	if ( rv != SECSuccess ) {
 724	    goto done;
 725	}
 726
 727	/* free the dbkey */
 728	sec_destroy_dbkey(dbkey);
 729	dbkey = NULL;
 730    }
 731    
 732    
 733    /* now traverse the database */
 734    ret = keydb_Seq(update, &key, &data, R_FIRST);
 735    if ( ret ) {
 736	goto done;
 737    }
 738    
 739    do {
 740	/* skip version record */
 741	if ( data.size > 1 ) {
 742	    /* skip salt */
 743	    if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
 744		if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
 745		    continue;
 746		}
 747	    }
 748	    /* skip pw check entry */
 749	    if ( key.size == checkKey.size ) {
 750		if ( PORT_Memcmp(key.data, checkKey.data, key.size) == 0 ) {
 751		    continue;
 752		}
 753	    }
 754
 755	    /* keys stored by nickname will have 0 as the last byte of the
 756	     * db key.  Other keys must be stored by modulus.  We will not
 757	     * update those because they are left over from a keygen that
 758	     * never resulted in a cert.
 759	     */
 760	    if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
 761		continue;
 762	    }
 763	    
 764	    dbkey = decode_dbkey(&data, 2);
 765	    if ( dbkey == NULL ) {
 766		continue;
 767	    }
 768
 769	    /* This puts the key into the new database with the same
 770	     * index (nickname) that it had before.  The second pass
 771	     * of the update will have the password.  It will decrypt
 772	     * and re-encrypt the entries using a new algorithm.
 773	     */
 774	    dbkey->nickname = (char *)key.data;
 775	    rv = put_dbkey(handle, &key, dbkey, PR_FALSE);
 776	    dbkey->nickname = NULL;
 777
 778	    sec_destroy_dbkey(dbkey);
 779	}
 780    } while ( keydb_Seq(update, &key, &data, R_NEXT) == 0 );
 781
 782    dbkey = NULL;
 783
 784done:
 785    /* sync the database */
 786    ret = keydb_Sync(handle, 0);
 787
 788    nsslowkey_CloseKeyDB(update);
 789    
 790    if ( oldSalt ) {
 791	SECITEM_FreeItem(oldSalt, PR_TRUE);
 792    }
 793    
 794    if ( dbkey ) {
 795	sec_destroy_dbkey(dbkey);
 796    }
 797
 798    return(SECSuccess);
 799}
 800
 801static SECStatus
 802openNewDB(const char *appName, const char *prefix, const char *dbname, 
 803	NSSLOWKEYDBHandle *handle, NSSLOWKEYDBNameFunc namecb, void *cbarg)
 804{
 805    SECStatus rv = SECFailure;
 806    int status = RDB_FAIL;
 807    char *updname = NULL;
 808    DB *updatedb = NULL;
 809    PRBool updated = PR_FALSE;
 810    int ret;
 811
 812    if (appName) {
 813	handle->db = rdbopen( appName, prefix, "key", NO_CREATE, &status);
 814    } else {
 815	handle->db = dbopen( dbname, NO_CREATE, 0600, DB_HASH, 0 );
 816    }
 817    /* if create fails then we lose */
 818    if ( handle->db == NULL ) {
 819	return (status == RDB_RETRY) ? SECWouldBlock: SECFailure;
 820    }
 821
 822    /* force a transactional read, which will verify that one and only one
 823     * process attempts the update. */
 824    if (nsslowkey_version(handle) == NSSLOWKEY_DB_FILE_VERSION) {
 825	/* someone else has already updated the database for us */
 826	db_InitComplete(handle->db);
 827	return SECSuccess;
 828    }
 829
 830    /*
 831     * if we are creating a multiaccess database, see if there is a
 832     * local database we can update from.
 833     */
 834    if (appName) {
 835        NSSLOWKEYDBHandle *updateHandle;
 836	updatedb = dbopen( dbname, NO_RDONLY, 0600, DB_HASH, 0 );
 837	if (!updatedb) {
 838	    goto noupdate;
 839	}
 840
 841	/* nsslowkey_version needs a full handle because it calls
 842         * the kdb_Get() function, which needs to lock.
 843         */
 844        updateHandle = nsslowkey_NewHandle(updatedb);
 845	if (!updateHandle) {
 846	    updatedb->close(updatedb);
 847	    goto noupdate;
 848	}
 849
 850	handle->version = nsslowkey_version(updateHandle);
 851	if (handle->version != NSSLOWKEY_DB_FILE_VERSION) {
 852	    nsslowkey_CloseKeyDB(updateHandle);
 853	    goto noupdate;
 854	}
 855
 856	/* copy the new DB from the old one */
 857	db_Copy(handle->db, updatedb);
 858	nsslowkey_CloseKeyDB(updateHandle);
 859	db_InitComplete(handle->db);
 860	return SECSuccess;
 861    }
 862noupdate:
 863
 864    /* update the version number */
 865    rv = makeGlobalVersion(handle);
 866    if ( rv != SECSuccess ) {
 867	goto loser;
 868    }
 869
 870    /*
 871     * try to update from v2 db
 872     */
 873    updname = (*namecb)(cbarg, 2);
 874    if ( updname != NULL ) {
 875	handle->updatedb = dbopen( updname, NO_RDONLY, 0600, DB_HASH, 0 );
 876        PORT_Free( updname );
 877
 878	if ( handle->updatedb ) {
 879	    /*
 880	     * Try to update the db using a null password.  If the db
 881	     * doesn't have a password, then this will work.  If it does
 882	     * have a password, then this will fail and we will do the
 883	     * update later
 884	     */
 885	    rv = nsslowkey_UpdateKeyDBPass1(handle);
 886	    if ( rv == SECSuccess ) {
 887		updated = PR_TRUE;
 888	    }
 889	}
 890	    
 891    }
 892
 893    /* we are using the old salt if we updated from an old db */
 894    if ( ! updated ) {
 895	rv = makeGlobalSalt(handle);
 896	if ( rv != SECSuccess ) {
 897	   goto loser;
 898	}
 899    }
 900	
 901    /* sync the database */
 902    ret = keydb_Sync(handle, 0);
 903    if ( ret ) {
 904	rv = SECFailure;
 905	goto loser;
 906    }
 907    rv = SECSuccess;
 908
 909loser:
 910    db_InitComplete(handle->db);
 911    return rv;
 912}
 913
 914
 915static DB *
 916openOldDB(const char *appName, const char *prefix, const char *dbname, 
 917					PRBool openflags) {
 918    DB *db = NULL;
 919
 920    if (appName) {
 921	db = rdbopen( appName, prefix, "key", openflags, NULL);
 922    } else {
 923	db = dbopen( dbname, openflags, 0600, DB_HASH, 0 );
 924    }
 925
 926    return db;
 927}
 928
 929/* check for correct version number */
 930static PRBool
 931verifyVersion(NSSLOWKEYDBHandle *handle)
 932{
 933    int version = nsslowkey_version(handle);
 934
 935    handle->version = version;
 936    if (version != NSSLOWKEY_DB_FILE_VERSION ) {
 937	if (handle->db) {
 938	    keydb_Close(handle);
 939	    handle->db = NULL;
 940	}
 941    }
 942    return handle->db != NULL;
 943}
 944
 945static NSSLOWKEYDBHandle *
 946nsslowkey_NewHandle(DB *dbHandle)
 947{
 948    NSSLOWKEYDBHandle *handle;
 949    handle = (NSSLOWKEYDBHandle *)PORT_ZAlloc (sizeof(NSSLOWKEYDBHandle));
 950    if (handle == NULL) {
 951	PORT_SetError (SEC_ERROR_NO_MEMORY);
 952	return NULL;
 953    }
 954
 955    handle->appname = NULL;
 956    handle->dbname = NULL;
 957    handle->global_salt = NULL;
 958    handle->updatedb = NULL;
 959    handle->db = dbHandle;
 960    handle->ref = 1;
 961    handle->lock = PZ_NewLock(nssILockKeyDB);
 962
 963    return handle;
 964}
 965
 966NSSLOWKEYDBHandle *
 967nsslowkey_OpenKeyDB(PRBool readOnly, const char *appName, const char *prefix,
 968				NSSLOWKEYDBNameFunc namecb, void *cbarg)
 969{
 970    NSSLOWKEYDBHandle *handle = NULL;
 971    SECStatus rv;
 972    int openflags;
 973    char *dbname = NULL;
 974
 975
 976    handle = nsslowkey_NewHandle(NULL);
 977
 978    openflags = readOnly ? NO_RDONLY : NO_RDWR;
 979
 980
 981    dbname = (*namecb)(cbarg, NSSLOWKEY_DB_FILE_VERSION);
 982    if ( dbname == NULL ) {
 983	goto loser;
 984    }
 985    handle->appname = appName ? PORT_Strdup(appName) : NULL ;
 986    handle->dbname = (appName == NULL) ? PORT_Strdup(dbname) : 
 987			(prefix ? PORT_Strdup(prefix) : NULL);
 988    handle->readOnly = readOnly;
 989
 990
 991
 992    handle->db = openOldDB(appName, prefix, dbname, openflags);
 993    if (handle->db) {
 994	verifyVersion(handle);
 995	if (handle->version == 255) {
 996	    goto loser;
 997	}
 998    }
 999
1000    /* if first open fails, try to create a new DB */
1001    if ( handle->db == NULL ) {
1002	if ( readOnly ) {
1003	    goto loser;
1004	}
1005
1006	rv = openNewDB(appName, prefix, dbname, handle, namecb, cbarg);
1007	/* two processes started to initialize the database at the same time.
1008	 * The multiprocess code blocked the second one, then had it retry to
1009	 * see if it can just open the database normally */
1010	if (rv == SECWouldBlock) {
1011	    handle->db = openOldDB(appName,prefix,dbname, openflags);
1012	    verifyVersion(handle);
1013	    if (handle->db == NULL) {
1014		goto loser;
1015	    }
1016	} else if (rv != SECSuccess) {
1017	    goto loser;
1018	}
1019    }
1020
1021    handle->global_salt = GetKeyDBGlobalSalt(handle);
1022    if ( dbname )
1023        PORT_Free( dbname );
1024    return handle;
1025
1026loser:
1027
1028    if ( dbname )
1029        PORT_Free( dbname );
1030    PORT_SetError(SEC_ERROR_BAD_DATABASE);
1031    nsslowkey_CloseKeyDB(handle);
1032    return NULL;
1033}
1034
1035/*
1036 * Close the database
1037 */
1038void
1039nsslowkey_CloseKeyDB(NSSLOWKEYDBHandle *handle)
1040{
1041    if (handle != NULL) {
1042	if (handle->db != NULL) {
1043	    keydb_Close(handle);
1044	}
1045	if (handle->updatedb) {
1046	    handle->updatedb->close(handle->updatedb);
1047        }
1048	if (handle->dbname) PORT_Free(handle->dbname);
1049	if (handle->appname) PORT_Free(handle->appname);
1050	if (handle->global_salt) {
1051	    SECITEM_FreeItem(handle->global_salt,PR_TRUE);
1052	}
1053	if (handle->lock != NULL) {
1054	    SKIP_AFTER_FORK(PZ_DestroyLock(handle->lock));
1055	}
1056	    
1057	PORT_Free(handle);
1058    }
1059}
1060
1061/* Get the key database version */
1062int
1063nsslowkey_GetKeyDBVersion(NSSLOWKEYDBHandle *handle)
1064{
1065    PORT_Assert(handle != NULL);
1066
1067    return handle->version;
1068}
1069
1070/*
1071 * Delete a private key that was stored in the database
1072 */
1073SECStatus
1074nsslowkey_DeleteKey(NSSLOWKEYDBHandle *handle, const SECItem *pubkey)
1075{
1076    DBT namekey;
1077    int ret;
1078
1079    if (handle == NULL) {
1080	PORT_SetError(SEC_ERROR_BAD_DATABASE);
1081	return(SECFailure);
1082    }
1083
1084    /* set up db key and data */
1085    namekey.data = pubkey->data;
1086    namekey.size = pubkey->len;
1087
1088    /* delete it from the database */
1089    ret = keydb_Del(handle, &namekey, 0);
1090    if ( ret ) {
1091	PORT_SetError(SEC_ERROR_BAD_DATABASE);
1092	return(SECFailure);
1093    }
1094
1095    /* sync the database */
1096    ret = keydb_Sync(handle, 0);
1097    if ( ret ) {
1098	PORT_SetError(SEC_ERROR_BAD_DATABASE);
1099	return(SECFailure);
1100    }
1101
1102    return(SECSuccess);
1103}
1104
1105/*
1106 * Store a key in the database, indexed by its public key modulus.(value!)
1107 */
1108SECStatus
1109nsslowkey_StoreKeyByPublicKey(NSSLOWKEYDBHandle *handle, 
1110			   NSSLOWKEYPrivateKey *privkey,
1111			   SECItem *pubKeyData,
1112			   char *nickname,
1113			   SDB *sdb)
1114{
1115    return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData, 
1116	     nickname, sdb, PR_FALSE);
1117}
1118
1119SECStatus
1120nsslowkey_UpdateNickname(NSSLOWKEYDBHandle *handle, 
1121			   NSSLOWKEYPrivateKey *privkey,
1122			   SECItem *pubKeyData,
1123			   char *nickname,
1124			   SDB *sdb)
1125{
1126    return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData, 
1127	     nickname, sdb, PR_TRUE);
1128}
1129
1130/* see if the symetric CKA_ID already Exists.
1131 */
1132PRBool
1133nsslowkey_KeyForIDExists(NSSLOWKEYDBHandle *handle, SECItem *id)
1134{
1135    DBT namekey;
1136    DBT dummy;
1137    int status;
1138
1139    namekey.data = (char *)id->data;
1140    namekey.size = id->len;
1141    status = keydb_Get(handle, &namekey, &dummy, 0);
1142    if ( status ) {
1143	return PR_FALSE;
1144    }
1145    
1146    return PR_TRUE;
1147}
1148
1149/* see if the public key for this cert is in the database filed
1150 * by modulus
1151 */
1152PRBool
1153nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle, NSSLOWCERTCertificate *cert)
1154{
1155    NSSLOWKEYPublicKey *pubkey = NULL;
1156    DBT namekey;
1157    DBT dummy;
1158    int status;
1159    
1160    /* get cert's public key */
1161    pubkey = nsslowcert_ExtractPublicKey(cert);
1162    if ( pubkey == NULL ) {
1163	return PR_FALSE;
1164    }
1165
1166    /* TNH - make key from NSSLOWKEYPublicKey */
1167    switch (pubkey->keyType) {
1168      case NSSLOWKEYRSAKey:
1169	namekey.data = pubkey->u.rsa.modulus.data;
1170	namekey.size = pubkey->u.rsa.modulus.len;
1171	break;
1172      case NSSLOWKEYDSAKey:
1173	namekey.data = pubkey->u.dsa.publicValue.data;
1174	namekey.size = pubkey->u.dsa.publicValue.len;
1175	break;
1176      case NSSLOWKEYDHKey:
1177	namekey.data = pubkey->u.dh.publicValue.data;
1178	namekey.size = pubkey->u.dh.publicValue.len;
1179	break;
1180#ifdef NSS_ENABLE_ECC
1181      case NSSLOWKEYECKey:
1182	namekey.data = pubkey->u.ec.publicValue.data;
1183	namekey.size = pubkey->u.ec.publicValue.len;
1184	break;
1185#endif /* NSS_ENABLE_ECC */
1186      default:
1187	/* XXX We don't do Fortezza or DH yet. */
1188	return PR_FALSE;
1189    }
1190
1191    if (handle->version != 3) {
1192	unsigned char buf[SHA1_LENGTH];
1193	SHA1_HashBuf(buf,namekey.data,namekey.size);
1194	/* NOTE: don't use pubkey after this! it's now thrashed */
1195	PORT_Memcpy(namekey.data,buf,sizeof(buf));
1196	namekey.size = sizeof(buf);
1197    }
1198
1199    status = keydb_Get(handle, &namekey, &dummy, 0);
1200    /* some databases have the key stored as a signed value */
1201    if (status) {
1202	unsigned char *buf = (unsigned char *)PORT_Alloc(namekey.size+1);
1203	if (buf) {
1204	    PORT_Memcpy(&buf[1], namekey.data, namekey.size);
1205	    buf[0] = 0;
1206	    namekey.data = buf;
1207	    namekey.size ++;
1208    	    status = keydb_Get(handle, &namekey, &dummy, 0);
1209	    PORT_Free(buf);
1210	}
1211    }
1212    lg_nsslowkey_DestroyPublicKey(pubkey);
1213    if ( status ) {
1214	return PR_FALSE;
1215    }
1216    
1217    return PR_TRUE;
1218}
1219
1220typedef struct NSSLowPasswordDataParamStr {
1221    SECItem salt;
1222    SECItem iter;
1223} NSSLowPasswordDataParam;
1224
1225static const SEC_ASN1Template NSSLOWPasswordParamTemplate[] =
1226{
1227    {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLowPasswordDataParam) },
1228    {SEC_ASN1_OCTET_STRING, offsetof(NSSLowPasswordDataParam, salt) },
1229    {SEC_ASN1_INTEGER, offsetof(NSSLowPasswordDataParam, iter) },
1230    {0}
1231};
1232struct LGEncryptedDataInfoStr {
1233    SECAlgorithmID algorithm;
1234    SECItem encryptedData;
1235};
1236typedef struct LGEncryptedDataInfoStr LGEncryptedDataInfo;
1237
1238const SEC_ASN1Template lg_EncryptedDataInfoTemplate[] = {
1239    { SEC_ASN1_SEQUENCE,
1240        0, NULL, sizeof(LGEncryptedDataInfo) },
1241    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
1242        offsetof(LGEncryptedDataInfo,algorithm),
1243        SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
1244    { SEC_ASN1_OCTET_STRING,
1245        offsetof(LGEncryptedDataInfo,encryptedData) },
1246    { 0 }
1247};
1248
1249static SECItem *
1250nsslowkey_EncodePW(SECOidTag alg, const SECItem *salt, SECItem *data)
1251{
1252    NSSLowPasswordDataParam param;
1253    LGEncryptedDataInfo edi;
1254    PLArenaPool *arena;
1255    unsigned char one = 1;
1256    SECItem *epw = NULL;
1257    SECItem *encParam;
1258    SECStatus rv;
1259
1260    param.salt = *salt;
1261    param.iter.type = siBuffer;  /* encode as signed integer */
1262    param.iter.data = &one;
1263    param.iter.len = 1;
1264    edi.encryptedData = *data;
1265
1266    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1267    if (arena == NULL) {
1268	return NULL;
1269    }
1270
1271    encParam = SEC_ASN1EncodeItem(arena, NULL, &param,
1272				  NSSLOWPasswordParamTemplate);
1273    if (encParam == NULL) {
1274	goto loser;
1275    }
1276    rv = SECOID_SetAlgorithmID(arena, &edi.algorithm, alg, encParam);
1277    if (rv != SECSuccess) {
1278	goto loser;
1279    }
1280    epw = SEC_ASN1EncodeItem(NULL, NULL, &edi, lg_EncryptedDataInfoTemplate);
1281
1282loser:
1283    PORT_FreeArena(arena, PR_FALSE);
1284    return epw;
1285}
1286
1287static SECItem *
1288nsslowkey_DecodePW(const SECItem *derData, SECOidTag *alg, SECItem *salt)
1289{
1290    NSSLowPasswordDataParam param;
1291    LGEncryptedDataInfo edi;
1292    PLArenaPool *arena;
1293    SECItem *pwe = NULL;
1294    SECStatus rv;
1295
1296    salt->data = NULL;
1297    param.iter.type = siBuffer;  /* decode as signed integer */
1298
1299    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1300    if (arena == NULL) {
1301	return NULL;
1302    }
1303
1304    rv = SEC_QuickDERDecodeItem(arena, &edi, lg_EncryptedDataInfoTemplate, 
1305						derData);
1306    if (rv != SECSuccess) {
1307	goto loser;
1308    }
1309    *alg = SECOID_GetAlgorithmTag(&edi.algorithm);
1310    rv = SEC_QuickDERDecodeItem(arena, &param, NSSLOWPasswordParamTemplate,
1311						&edi.algorithm.parameters);
1312    if (rv != SECSuccess) {
1313	goto loser;
1314    }
1315    rv = SECITEM_CopyItem(NULL, salt, &param.salt);
1316    if (rv != SECSuccess) {
1317	goto loser;
1318    }
1319    pwe = SECITEM_DupItem(&edi.encryptedData);
1320
1321loser:
1322    if (!pwe && salt->data) {
1323	PORT_Free(salt->data);
1324	salt->data = NULL;
1325    }
1326    PORT_FreeArena(arena, PR_FALSE);
1327    return pwe;
1328}
1329
1330
1331/*
1332 * check to see if the user has a password
1333 */
1334static SECStatus
1335nsslowkey_GetPWCheckEntry(NSSLOWKEYDBHandle *handle,NSSLOWKEYPasswordEntry *entry)
1336{
1337    DBT checkkey; /*, checkdata; */
1338    NSSLOWKEYDBKey *dbkey = NULL;
1339    SECItem   *global_salt = NULL; 
1340    SECItem   *item = NULL; 
1341    SECItem   entryData, oid;
1342    SECItem   none = { siBuffer, NULL, 0 };
1343    SECStatus rv = SECFailure;
1344    SECOidTag algorithm;
1345
1346    if (handle == NULL) {
1347	/* PORT_SetError */
1348	return(SECFailure);
1349    }
1350
1351    global_salt = GetKeyDBGlobalSalt(handle);
1352    if (!global_salt) {
1353	global_salt = &none;
1354    }
1355    if (global_salt->len > sizeof(entry->data)) {
1356	/* PORT_SetError */
1357	goto loser;
1358    }
1359	
1360    PORT_Memcpy(entry->data, global_salt->data, global_salt->len);
1361    entry->salt.data = entry->data;
1362    entry->salt.len = global_salt->len;
1363    entry->value.data = &entry->data[entry->salt.len];
1364
1365    checkkey.data = KEYDB_PW_CHECK_STRING;
1366    checkkey.size = KEYDB_PW_CHECK_LEN;
1367    dbkey = get_dbkey(handle, &checkkey);
1368    if (dbkey == NULL) {
1369	/* handle 'FAKE' check here */
1370	goto loser;
1371    }
1372
1373    oid.len = dbkey->derPK.data[0];
1374    oid.data = &dbkey->derPK.data[1];
1375
1376    if (dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 +oid.len)) {
1377	goto loser;
1378    }
1379    algorithm = SECOID_FindOIDTag(&oid);
1380    entryData.type = siBuffer;
1381    entryData.len = dbkey->derPK.len - (oid.len+1);
1382    entryData.data = &dbkey->derPK.data[oid.len+1];
1383
1384    item = nsslowkey_EncodePW(algorithm, &dbkey->salt, &entryData);
1385    if (!item || (item->len + entry->salt.len) > sizeof(entry->data)) {
1386	goto loser;
1387    }
1388    PORT_Memcpy(entry->value.data, item->data, item->len);
1389    entry->value.len = item->len;
1390    rv = SECSuccess;
1391
1392loser:
1393    if (item) {
1394	SECITEM_FreeItem(item, PR_TRUE);
1395    }
1396    if (dbkey) {
1397 	sec_destroy_dbkey(dbkey);
1398    }
1399    if (global_salt != &none) {
1400	SECITEM_FreeItem(global_salt,PR_TRUE);
1401    }
1402    return rv;
1403}
1404
1405/*
1406 * check to see if the user has a password
1407 */
1408static SECStatus
1409nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle,NSSLOWKEYPasswordEntry *entry)
1410{
1411    DBT checkkey;
1412    NSSLOWKEYDBKey *dbkey = NULL;
1413    SECItem   *item = NULL; 
1414    SECItem   salt; 
1415    SECOidTag algid;
1416    SECStatus rv = SECFailure;
1417    PLArenaPool *arena;
1418    int ret;
1419
1420    if (handle == NULL) {
1421	/* PORT_SetError */
1422	return(SECFailure);
1423    }
1424
1425    checkkey.data = KEYDB_PW_CHECK_STRING;
1426    checkkey.size = KEYDB_PW_CHECK_LEN;
1427
1428    salt.data = NULL;
1429    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1430    if (arena == NULL) {
1431	return SECFailure;
1432    }
1433
1434    item = nsslowkey_DecodePW(&entry->value, &algid, &salt);
1435    if (item == NULL) {
1436	goto loser;
1437    }
1438
1439    dbkey = PORT_ArenaZNew(arena, NSSLOWKEYDBKey);
1440    if (dbkey == NULL) {
1441	goto loser;
1442    }
1443
1444    dbkey->arena = arena;
1445
1446    rv = SECITEM_CopyItem(arena, &dbkey->salt, &salt);
1447    if (rv != SECSuccess) {
1448	goto loser;
1449    }
1450
1451    rv = encodePWCheckEntry(arena, &dbkey->derPK, algid, item);
1452    if (rv != SECSuccess) {
1453	goto loser;
1454    }
1455
1456    rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE);
1457    if (rv != SECSuccess) {
1458	goto loser;
1459    }
1460
1461    if (handle->global_salt) {
1462	SECITEM_FreeItem(handle->global_salt, PR_TRUE);
1463	handle->global_salt = NULL;
1464    }
1465    rv = StoreKeyDBGlobalSalt(handle, &entry->salt);
1466    if (rv != SECSuccess) {
1467	goto loser;
1468    }
1469    ret = keydb_Sync(handle, 0);
1470    if ( ret ) {
1471	rv = SECFailure;
1472	goto loser;
1473    }
1474    handle->global_salt = GetKeyDBGlobalSalt(handle);
1475
1476loser:
1477    if (item) {
1478	SECITEM_FreeItem(item, PR_TRUE);
1479    }
1480    if (arena) {
1481	PORT_FreeArena(arena, PR_TRUE);
1482    }
1483    if (salt.data) {
1484	PORT_Free(salt.data);
1485    }
1486    return rv;
1487}
1488
1489#ifdef EC_DEBUG
1490#define SEC_PRINT(str1, str2, num, sitem) \
1491    printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
1492            str1, str2, num, sitem->len); \
1493    for (i = 0; i < sitem->len; i++) { \
1494	    printf("%02x:", sitem->data[i]); \
1495    } \
1496    printf("\n") 
1497#else
1498#define SEC_PRINT(a, b, c, d) 
1499#endif /* EC_DEBUG */
1500
1501
1502SECStatus 
1503seckey_encrypt_private_key( PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk, 
1504			    SDB *sdbpw, SECItem *result)
1505{
1506    NSSLOWKEYPrivateKeyInfo *pki = NULL;
1507    SECStatus rv = SECFailure;
1508    PLArenaPool *temparena = NULL;
1509    SECItem *der_item = NULL;
1510    SECItem *cipherText = NULL;
1511    SECItem *dummy = NULL;
1512#ifdef NSS_ENABLE_ECC
1513    SECItem *fordebug = NULL;
1514    int savelen;
1515#endif
1516
1517    temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
1518    if(temparena == NULL)
1519	goto loser;
1520
1521    /* allocate structures */
1522    pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena, 
1523	sizeof(NSSLOWKEYPrivateKeyInfo));
1524    der_item = (SECItem *)PORT_ArenaZAlloc(temparena, sizeof(SECItem));
1525    if((pki == NULL) || (der_item == NULL))
1526	goto loser;
1527
1528
1529    /* setup private key info */
1530    dummy = SEC_ASN1EncodeInteger(temparena, &(pki->version), 
1531	NSSLOWKEY_PRIVATE_KEY_INFO_VERSION);
1532    if(dummy == NULL)
1533	goto loser;
1534
1535    /* Encode the key, and set the algorithm (with params) */
1536    switch (pk->keyType) {
1537      case NSSLOWKEYRSAKey:
1538        lg_prepare_low_rsa_priv_key_for_asn1(pk);
1539	dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, 
1540				   lg_nsslowkey_RSAPrivateKeyTemplate);
1541	if (dummy == NULL) {
1542	    rv = SECFailure;
1543	    goto loser;
1544	}
1545	
1546	rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), 
1547				   SEC_OID_PKCS1_RSA_ENCRYPTION, 0);
1548	if (rv == SECFailure) {
1549	    goto loser;
1550	}
1551	
1552	break;
1553      case NSSLOWKEYDSAKey:
1554        lg_prepare_low_dsa_priv_key_for_asn1(pk);
1555	dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
1556				   lg_nsslowkey_DSAPrivateKeyTemplate);
1557	if (dummy == NULL) {
1558	    rv = SECFailure;
1559	    goto loser;
1560	}
1561	
1562        lg_prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
1563	dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params,
1564				   lg_nsslowkey_PQGParamsTemplate);
1565	if (dummy == NULL) {
1566	    rv = SECFailure;
1567	    goto loser;
1568	}
1569	
1570	rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
1571				   SEC_OID_ANSIX9_DSA_SIGNATURE, dummy);
1572	if (rv == SECFailure) {
1573	    goto loser;
1574	}
1575	
1576	break;
1577      case NSSLOWKEYDHKey:
1578        lg_prepare_low_dh_priv_key_for_asn1(pk);
1579	dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
1580				   lg_nsslowkey_DHPrivateKeyTemplate);
1581	if (dummy == NULL) {
1582	    rv = SECFailure;
1583	    goto loser;
1584	}
1585
1586	rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
1587				   SEC_OID_X942_DIFFIE_HELMAN_KEY, dummy);
1588	if (rv == SECFailure) {
1589	    goto loser;
1590	}
1591	break;
1592#ifdef NSS_ENABLE_ECC
1593      case NSSLOWKEYECKey:
1594	lg_prepare_low_ec_priv_key_for_asn1(pk);
1595	/* Public value is encoded as a bit string so adjust length
1596	 * to be in bits before ASN encoding and readjust 
1597	 * immediately after.
1598	 *
1599	 * Since the SECG specification recommends not including the
1600	 * parameters as part of ECPrivateKey, we zero out the curveOID
1601	 * length before encoding and restore it later.
1602	 */
1603	pk->u.ec.publicValue.len <<= 3;
1604	savelen = pk->u.ec.ecParams.curveOID.len;
1605	pk->u.ec.ecParams.curveOID.len = 0;
1606	dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
1607				   lg_nsslowkey_ECPrivateKeyTemplate);
1608	pk->u.ec.ecParams.curveOID.len = savelen;
1609	pk->u.ec.publicValue.len >>= 3;
1610
1611	if (dummy == NULL) {
1612	    rv = SECFailure;
1613	    goto loser;
1614	}
1615
1616	dummy = &pk->u.ec.ecParams.DEREncoding;
1617
1618	/* At this point dummy should contain the encoded params */
1619	rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
1620				   SEC_OID_ANSIX962_EC_PUBLIC_KEY, dummy);
1621
1622	if (rv == SECFailure) {
1623	    goto loser;
1624	}
1625	
1626	fordebug = &(pki->privateKey);
1627	SEC_PRINT("seckey_encrypt_private_key()", "PrivateKey", 
1628		  pk->keyType, fordebug);
1629
1630	break;
1631#endif /* NSS_ENABLE_ECC */
1632      default:
1633	/* We don't support DH or Fortezza private keys yet */
1634	PORT_Assert(PR_FALSE);
1635	break;
1636    }
1637
1638    /* setup encrypted private key info */
1639    dummy = SEC_ASN1EncodeItem(temparena, der_item, pki, 
1640	lg_nsslowkey_PrivateKeyInfoTemplate);
1641
1642    SEC_PRINT("seckey_encrypt_private_key()", "PrivateKeyInfo", 
1643	      pk->keyType, der_item);
1644
1645    if(dummy == NULL) {
1646	rv = SECFailure;
1647	goto loser;
1648    }
1649
1650    rv = lg_util_encrypt(temparena, sdbpw, dummy, &cipherText);
1651    if (rv != SECSuccess) {
1652	goto loser;
1653    }
1654
1655    rv = SECITEM_CopyItem ( permarena, result, cipherText);
1656
1657loser:
1658
1659    if(temparena != NULL)
1660	PORT_FreeArena(temparena, PR_TRUE);
1661
1662    return rv;
1663}
1664
1665static SECStatus 
1666seckey_put_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, SDB *sdbpw,
1667		       NSSLOWKEYPrivateKey *pk, char *nickname, PRBool update)
1668{
1669    NSSLOWKEYDBKey *dbkey = NULL;
1670    PLArenaPool *arena = NULL;
1671    SECStatus rv = SECFailure;
1672
1673    if((keydb == NULL) || (index == NULL) || (sdbpw == NULL) ||
1674	(pk == NULL))
1675	return SECFailure;
1676	
1677    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
1678    if(arena == NULL)
1679	return SECFailure;
1680
1681    dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
1682    if(dbkey == NULL)
1683	goto loser;
1684    dbkey->arena = arena;
1685    dbkey->nickname = nickname;
1686
1687    rv = seckey_encrypt_private_key(arena, pk, sdbpw, &dbkey->derPK);
1688    if(rv != SECSuccess)
1689	goto loser;
1690
1691    rv = put_dbkey(keydb, index, dbkey, update);
1692
1693    /* let success fall through */
1694loser:
1695    if(arena != NULL)
1696	PORT_FreeArena(arena, PR_TRUE);
1697
1698    return rv;
1699}
1700
1701/*
1702 * Store a key in the database, indexed by its public key modulus.
1703 * Note that the nickname is optional.  It was only used by keyutil.
1704 */
1705SECStatus
1706nsslowkey_StoreKeyByPublicKeyAlg(NSSLOWKEYDBHandle *handle, 
1707			      NSSLOWKEYPrivateKey *privkey,
1708			      SECItem *pubKeyData,
1709			      char *nickname,
1710			      SDB *sdbpw,
1711                              PRBool update)
1712{
1713    DBT namekey;
1714    SECStatus rv;
1715
1716    if (handle == NULL) {
1717	PORT_SetError(SEC_ERROR_BAD_DATABASE);
1718	return(SECFailure);
1719    }
1720
1721    /* set up db key and data */
1722    namekey.data = pubKeyData->data;
1723    namekey.size = pubKeyData->len;
1724
1725    /* encrypt the private key */
1726    rv = seckey_put_private_key(handle, &namekey, sdbpw, privkey, nickname,
1727				update);
1728    
1729    return(rv);
1730}
1731
1732static NSSLOWKEYPrivateKey *
1733seckey_decrypt_private_key(SECItem*epki,
1734			   SDB *sdbpw)
1735{
1736    NSSLOWKEYPrivateKey *pk = NULL;
1737    NSSLOWKEYPrivateKeyInfo *pki = NULL;
1738    SECStatus rv = SECFailure;
1739    PLArenaPool *temparena = NULL, *permarena = NULL;
1740    SECItem *dest = NULL;
1741#ifdef NSS_ENABLE_ECC
1742    SECItem *fordebug = NULL;
1743#endif
1744
1745    if((epki == NULL) || (sdbpw == NULL))
1746	goto loser;
1747
1748    temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
1749    permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
1750    if((temparena == NULL) || (permarena == NULL))
1751	goto loser;
1752
1753    /* allocate temporary items */
1754    pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena, 
1755	sizeof(NSSLOWKEYPrivateKeyInfo));
1756
1757    /* allocate permanent arena items */
1758    pk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(permarena,
1759	sizeof(NSSLOWKEYPrivateKey));
1760
1761    if((pk == NULL) || (pki == NULL))
1762	goto loser;
1763
1764    pk->arena = permarena;
1765
1766    rv = lg_util_decrypt(sdbpw, epki, &dest);
1767    if (rv != SECSuccess) {
1768	goto loser;
1769    }
1770	
1771    if(dest != NULL)
1772    {
1773        SECItem newPrivateKey;
1774        SECItem newAlgParms;
1775
1776        SEC_PRINT("seckey_decrypt_private_key()", "PrivateKeyInfo", -1,
1777		  dest);
1778
1779	rv = SEC_QuickDERDecodeItem(temparena, pki, 
1780	    lg_nsslowkey_PrivateKeyInfoTemplate, dest);
1781	if(rv == SECSuccess)
1782	{
1783	    switch(SECOID_GetAlgorithmTag(&pki->algorithm)) {
1784	      case SEC_OID_X500_RSA_ENCRYPTION:
1785	      case SEC_OID_PKCS1_RSA_ENCRYPTION:
1786		pk->keyType = NSSLOWKEYRSAKey;
1787		lg_prepare_low_rsa_priv_key_for_asn1(pk);
1788                if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
1789                    &pki->privateKey) ) break;
1790		rv = SEC_QuickDERDecodeItem(permarena, pk,
1791					lg_nsslowkey_RSAPrivateKeyTemplate,
1792					&newPrivateKey);
1793		break;
1794	      case SEC_OID_ANSIX9_DSA_SIGNATURE:
1795		pk->keyType = NSSLOWKEYDSAKey;
1796		lg_prepare_low_dsa_priv_key_for_asn1(pk);
1797                if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
1798                    &pki->privateKey) ) break;
1799		rv = SEC_QuickDERDecodeItem(permarena, pk,
1800					lg_nsslowkey_DSAPrivateKeyTemplate,
1801					&newPrivateKey);
1802		if (rv != SECSuccess)
1803		    goto loser;
1804		lg_prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
1805                if (SECSuccess != SECITEM_CopyItem(permarena, &newAlgParms,
1806                    &pki->algorithm.parameters) ) break;
1807		rv = SEC_QuickDERDecodeItem(permarena, &pk->u.dsa.params,
1808					lg_nsslowkey_PQGParamsTemplate,
1809					&newAlgParms);
1810		break;
1811	      case SEC_OID_X942_DIFFIE_HELMAN_KEY:
1812		pk->keyType = NSSLOWKEYDHKey;
1813		lg_prepare_low_dh_priv_key_for_asn1(pk);
1814                if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
1815                    &pki->privateKey) ) break;
1816		rv = SEC_QuickDERDecodeItem(permarena, pk,
1817					lg_nsslowkey_DHPrivateKeyTemplate,
1818					&newPrivateKey);
1819		break;
1820#ifdef NSS_ENABLE_ECC
1821	      case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
1822		pk->keyType = NSSLOWKEYECKey;
1823		lg_prepare_low_ec_priv_key_for_asn1(pk);
1824
1825		fordebug = &pki->privateKey;
1826		SEC_PRINT("seckey_decrypt_private_key()", "PrivateKey", 
1827			  pk->keyType, fordebug);
1828                if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
1829                    &pki->privateKey) ) break;
1830		rv = SEC_QuickDERDecodeItem(permarena, pk,
1831					lg_nsslowkey_ECPrivateKeyTemplate,
1832					&newPrivateKey);
1833		if (rv != SECSuccess)
1834		    goto loser;
1835
1836		lg_prepare_low_ecparams_for_asn1(&pk->u.ec.ecParams);
1837
1838		rv = SECITEM_CopyItem(permarena, 
1839		    &pk->u.ec.ecParams.DEREncoding, 
1840		    &pki->algorithm.parameters);
1841
1842		if (rv != SECSuccess)
1843		    goto loser;
1844
1845		/* Fill out the rest of EC params */
1846		rv = LGEC_FillParams(permarena, &pk->u.ec.ecParams.DEREncoding,
1847				   &pk->u.ec.ecParams);
1848
1849		if (rv != SECSuccess)
1850		    goto loser;
1851
1852		if (pk->u.ec.publicValue.len != 0) {
1853		    pk->u.ec.publicValue.len >>= 3;
1854		}
1855
1856		break;
1857#endif /* NSS_ENABLE_ECC */
1858	      default:
1859		rv = SECFailure;
1860		break;
1861	    }
1862	}
1863	else if(PORT_GetError() == SEC_ERROR_BAD_DER)
1864	{
1865	    PORT_SetError(SEC_ERROR_BAD_PASSWORD);
1866	    goto loser;
1867	}
1868    }
1869
1870    /* let success fall through */
1871loser:
1872    if(temparena != NULL)
1873	PORT_FreeArena(temparena, PR_TRUE);
1874    if(dest != NULL)
1875	SECITEM_ZfreeItem(dest, PR_TRUE);
1876
1877    if(rv != SECSuccess)
1878    {
1879	if(permarena != NULL)
1880	    PORT_FreeArena(permarena, PR_TRUE);
1881	pk = NULL;
1882    }
1883
1884    return pk;
1885}
1886
1887static NSSLOWKEYPrivateKey *
1888seckey_decode_encrypted_private_key(NSSLOWKEYDBKey *dbkey, SDB *sdbpw)
1889{
1890    if( ( dbkey == NULL ) || ( sdbpw == NULL ) ) {
1891	return NULL;
1892    }
1893
1894    return seckey_decrypt_private_key(&(dbkey->derPK), sdbpw);
1895}
1896
1897static NSSLOWKEYPrivateKey *
1898seckey_get_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, char **nickname,
1899		       SDB *sdbpw)
1900{
1901    NSSLOWKEYDBKey *dbkey = NULL;
1902    NSSLOWKEYPrivateKey *pk = NULL;
1903
1904    if( ( keydb == NULL ) || ( index == NULL ) || ( sdbpw == NULL ) ) {
1905	return NULL;
1906    }
1907
1908    dbkey = get_dbkey(keydb, index);
1909    if(dbkey == NULL) {
1910	goto loser;
1911    }
1912    
1913    if ( nickname ) {
1914	if ( dbkey->nickname && ( dbkey->nickname[0] != 0 ) ) {
1915	    *nickname = PORT_Strdup(dbkey->nickname);
1916	} else {
1917	    *nickname = NULL;
1918	}
1919    }
1920    
1921    pk = seckey_decode_encrypted_private_key(dbkey, sdbpw);
1922    
1923    /* let success fall through */
1924loser:
1925
1926    if ( dbkey != NULL ) {
1927	sec_destroy_dbkey(dbkey);
1928    }
1929
1930    return pk;
1931}
1932
1933/*
1934 * Find a key in the database, indexed by its public key modulus
1935 * This is used to find keys that have been stored before their
1936 * certificate arrives.  Once the certificate arrives the key
1937 * is looked up by the public modulus in the certificate, and the
1938 * re-stored by its nickname.
1939 */
1940NSSLOWKEYPrivateKey *
1941nsslowkey_FindKeyByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus,
1942			  				 SDB *sdbpw)
1943{
1944    DBT namekey;
1945    NSSLOWKEYPrivateKey *pk = NULL;
1946
1947    if (handle == NULL) {
1948	PORT_SetError(SEC_ERROR_BAD_DATABASE);
1949	return NULL;
1950    }
1951
1952    /* set up db key */
1953    namekey.data = modulus->data;
1954    namekey.size = modulus->len;
1955
1956    pk = seckey_get_private_key(handle, &namekey, NULL, sdbpw);
1957    
1958    /* no need to free dbkey, since its on the stack, and the data it
1959     * points to is owned by the database
1960     */
1961    return(pk);
1962}
1963
1964char *
1965nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle, 
1966					SECItem *modulus, SDB *sdbpw)
1967{
1968    DBT namekey;
1969    NSSLOWKEYPrivateKey *pk = NULL;
1970    char *nickname = NULL;
1971
1972    if (handle == NULL) {
1973	PORT_SetError(SEC_ERROR_BAD_DATABASE);
1974	return NULL;
1975    }
1976
1977    /* set up db key */
1978    namekey.data = modulus->data;
1979    namekey.size = modulus->len;
1980
1981    pk = seckey_get_private_key(handle, &namekey, &nickname, sdbpw);
1982    if (pk) {
1983	lg_nsslowkey_DestroyPrivateKey(pk);
1984    }
1985    
1986    /* no need to free dbkey, since its on the stack, and the data it
1987     * points to is owned by the database
1988     */
1989    return(nickname);
1990}
1991/* ===== ENCODING ROUTINES ===== */
1992
1993static SECStatus
1994encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
1995		   SECItem *encCheck)
1996{
1997    SECOidData *oidData;
1998    SECStatus rv;
1999    
2000    oidData = SECOID_FindOIDByTag(alg);
2001    if ( oidData == NULL ) {
2002	rv = SECFailure;
2003	goto loser;
2004    }
2005
2006    entry->len = 1 + oidData->oid.len + encCheck->len;
2007    if ( arena ) {
2008	entry->data = (unsigned char *)PORT_ArenaAlloc(arena, entry->len);
2009    } else {
2010	entry->data = (unsigned char *)PORT_Alloc(entry->len);
2011    }
2012    
2013    if ( entry->data == NULL ) {
2014	goto loser;
2015    }
2016	
2017    /* first length of oid */
2018    entry->data[0] = (unsigned char)oidData->oid.len;
2019    /* next oid itself */
2020    PORT_Memcpy(&entry->data[1], oidData->oid.data, oidData->oid.len);
2021    /* finally the encrypted check string */
2022    PORT_Memcpy(&entry->data[1+oidData->oid.len], encCheck->data,
2023		encCheck->len);
2024
2025    return(SECSuccess);
2026
2027loser:
2028    return(SECFailure);
2029}
2030    
2031
2032#define MAX_DB_SIZE 0xffff 
2033/*
2034 * Clear out all the keys in the existing database
2035 */
2036static SECStatus
2037nsslowkey_ResetKeyDB(NSSLOWKEYDBHandle *handle)
2038{
2039    SECStatus rv;
2040    int ret;
2041    int errors = 0;
2042
2043    if ( handle->db == NULL ) {
2044	return(SECSuccess);
2045    }
2046
2047    if (handle->readOnly) {
2048	/* set an error code */
2049	return SECFailure;
2050     }
2051
2052    if (handle->appname == NULL && handle->dbname == NULL) {
2053	return SECFailure;
2054    }
2055
2056    keydb_Close(handle);
2057    if (handle->appname) {
2058	hand

Large files files are truncated, but you can click here to view the full file