1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/cmd/smimetools/cmsutil.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1617 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * cmsutil -- A command to work with CMS data 1.10 + */ 1.11 + 1.12 +#include "nspr.h" 1.13 +#include "secutil.h" 1.14 +#include "plgetopt.h" 1.15 +#include "secpkcs7.h" 1.16 +#include "cert.h" 1.17 +#include "certdb.h" 1.18 +#include "secoid.h" 1.19 +#include "cms.h" 1.20 +#include "nss.h" 1.21 +#include "smime.h" 1.22 +#include "pk11func.h" 1.23 + 1.24 +#if defined(XP_UNIX) 1.25 +#include <unistd.h> 1.26 +#endif 1.27 + 1.28 +#if defined(_WIN32) 1.29 +#include "fcntl.h" 1.30 +#include "io.h" 1.31 +#endif 1.32 + 1.33 +#include <stdio.h> 1.34 +#include <string.h> 1.35 + 1.36 +char *progName = NULL; 1.37 +static int cms_verbose = 0; 1.38 +static secuPWData pwdata = { PW_NONE, 0 }; 1.39 +static PK11PasswordFunc pwcb = NULL; 1.40 +static void *pwcb_arg = NULL; 1.41 + 1.42 + 1.43 +/* XXX stolen from cmsarray.c 1.44 + * nss_CMSArray_Count - count number of elements in array 1.45 + */ 1.46 +int 1.47 +nss_CMSArray_Count(void **array) 1.48 +{ 1.49 + int n = 0; 1.50 + if (array == NULL) 1.51 + return 0; 1.52 + while (*array++ != NULL) 1.53 + n++; 1.54 + return n; 1.55 +} 1.56 + 1.57 +static SECStatus 1.58 +DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input, 1.59 + SECAlgorithmID **algids) 1.60 +{ 1.61 + NSSCMSDigestContext *digcx; 1.62 + SECStatus rv; 1.63 + 1.64 + digcx = NSS_CMSDigestContext_StartMultiple(algids); 1.65 + if (digcx == NULL) 1.66 + return SECFailure; 1.67 + 1.68 + NSS_CMSDigestContext_Update(digcx, input->data, input->len); 1.69 + 1.70 + rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests); 1.71 + return rv; 1.72 +} 1.73 + 1.74 + 1.75 +static void 1.76 +Usage(char *progName) 1.77 +{ 1.78 + fprintf(stderr, 1.79 +"Usage: %s [-C|-D|-E|-O|-S] [<options>] [-d dbdir] [-u certusage]\n" 1.80 +" -C create a CMS encrypted data message\n" 1.81 +" -D decode a CMS message\n" 1.82 +" -b decode a batch of files named in infile\n" 1.83 +" -c content use this detached content\n" 1.84 +" -n suppress output of content\n" 1.85 +" -h num display num levels of CMS message info as email headers\n" 1.86 +" -k keep decoded encryption certs in perm cert db\n" 1.87 +" -E create a CMS enveloped data message\n" 1.88 +" -r id,... create envelope for these recipients,\n" 1.89 +" where id can be a certificate nickname or email address\n" 1.90 +" -S create a CMS signed data message\n" 1.91 +" -G include a signing time attribute\n" 1.92 +" -H hash use hash (default:SHA1)\n" 1.93 +" -N nick use certificate named \"nick\" for signing\n" 1.94 +" -P include a SMIMECapabilities attribute\n" 1.95 +" -T do not include content in CMS message\n" 1.96 +" -Y nick include a EncryptionKeyPreference attribute with cert\n" 1.97 +" (use \"NONE\" to omit)\n" 1.98 +" -O create a CMS signed message containing only certificates\n" 1.99 +" General Options:\n" 1.100 +" -d dbdir key/cert database directory (default: ~/.netscape)\n" 1.101 +" -e envelope enveloped data message in this file is used for bulk key\n" 1.102 +" -i infile use infile as source of data (default: stdin)\n" 1.103 +" -o outfile use outfile as destination of data (default: stdout)\n" 1.104 +" -p password use password as key db password (default: prompt)\n" 1.105 +" -f pwfile use password file to set password on all PKCS#11 tokens)\n" 1.106 +" -u certusage set type of certificate usage (default: certUsageEmailSigner)\n" 1.107 +" -v print debugging information\n" 1.108 +"\n" 1.109 +"Cert usage codes:\n", 1.110 + progName); 1.111 + fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " "); 1.112 + fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " "); 1.113 + fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " "); 1.114 + fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " "); 1.115 + fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " "); 1.116 + fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " "); 1.117 + fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " "); 1.118 + fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " "); 1.119 + fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " "); 1.120 + fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " "); 1.121 + fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " "); 1.122 + fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " "); 1.123 + 1.124 + exit(-1); 1.125 +} 1.126 + 1.127 +struct optionsStr { 1.128 + char *pwfile; 1.129 + char *password; 1.130 + SECCertUsage certUsage; 1.131 + CERTCertDBHandle *certHandle; 1.132 +}; 1.133 + 1.134 +struct decodeOptionsStr { 1.135 + struct optionsStr *options; 1.136 + SECItem content; 1.137 + int headerLevel; 1.138 + PRBool suppressContent; 1.139 + NSSCMSGetDecryptKeyCallback dkcb; 1.140 + PK11SymKey *bulkkey; 1.141 + PRBool keepCerts; 1.142 +}; 1.143 + 1.144 +struct signOptionsStr { 1.145 + struct optionsStr *options; 1.146 + char *nickname; 1.147 + char *encryptionKeyPreferenceNick; 1.148 + PRBool signingTime; 1.149 + PRBool smimeProfile; 1.150 + PRBool detached; 1.151 + SECOidTag hashAlgTag; 1.152 +}; 1.153 + 1.154 +struct envelopeOptionsStr { 1.155 + struct optionsStr *options; 1.156 + char **recipients; 1.157 +}; 1.158 + 1.159 +struct certsonlyOptionsStr { 1.160 + struct optionsStr *options; 1.161 + char **recipients; 1.162 +}; 1.163 + 1.164 +struct encryptOptionsStr { 1.165 + struct optionsStr *options; 1.166 + char **recipients; 1.167 + NSSCMSMessage *envmsg; 1.168 + SECItem *input; 1.169 + FILE *outfile; 1.170 + PRFileDesc *envFile; 1.171 + PK11SymKey *bulkkey; 1.172 + SECOidTag bulkalgtag; 1.173 + int keysize; 1.174 +}; 1.175 + 1.176 +static NSSCMSMessage * 1.177 +decode(FILE *out, SECItem *input, const struct decodeOptionsStr *decodeOptions) 1.178 +{ 1.179 + NSSCMSDecoderContext *dcx; 1.180 + SECStatus rv; 1.181 + NSSCMSMessage *cmsg; 1.182 + int nlevels, i; 1.183 + SECItem sitem = { 0, 0, 0 }; 1.184 + 1.185 + PORT_SetError(0); 1.186 + dcx = NSS_CMSDecoder_Start(NULL, 1.187 + NULL, NULL, /* content callback */ 1.188 + pwcb, pwcb_arg, /* password callback */ 1.189 + decodeOptions->dkcb, /* decrypt key callback */ 1.190 + decodeOptions->bulkkey); 1.191 + if (dcx == NULL) { 1.192 + fprintf(stderr, "%s: failed to set up message decoder.\n", progName); 1.193 + return NULL; 1.194 + } 1.195 + rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len); 1.196 + if (rv != SECSuccess) { 1.197 + fprintf(stderr, "%s: failed to decode message.\n", progName); 1.198 + NSS_CMSDecoder_Cancel(dcx); 1.199 + return NULL; 1.200 + } 1.201 + cmsg = NSS_CMSDecoder_Finish(dcx); 1.202 + if (cmsg == NULL) { 1.203 + fprintf(stderr, "%s: failed to decode message.\n", progName); 1.204 + return NULL; 1.205 + } 1.206 + 1.207 + if (decodeOptions->headerLevel >= 0) { 1.208 + /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/ 1.209 + fprintf(out, "SMIME: "); 1.210 + } 1.211 + 1.212 + nlevels = NSS_CMSMessage_ContentLevelCount(cmsg); 1.213 + for (i = 0; i < nlevels; i++) { 1.214 + NSSCMSContentInfo *cinfo; 1.215 + SECOidTag typetag; 1.216 + 1.217 + cinfo = NSS_CMSMessage_ContentLevel(cmsg, i); 1.218 + typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 1.219 + 1.220 + if (decodeOptions->headerLevel >= 0) 1.221 + fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i); 1.222 + 1.223 + switch (typetag) { 1.224 + case SEC_OID_PKCS7_SIGNED_DATA: 1.225 + { 1.226 + NSSCMSSignedData *sigd = NULL; 1.227 + SECItem **digests; 1.228 + int nsigners; 1.229 + int j; 1.230 + 1.231 + if (decodeOptions->headerLevel >= 0) 1.232 + fprintf(out, "type=signedData; "); 1.233 + sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo); 1.234 + if (sigd == NULL) { 1.235 + SECU_PrintError(progName, "signedData component missing"); 1.236 + goto loser; 1.237 + } 1.238 + 1.239 + /* if we have a content file, but no digests for this signedData */ 1.240 + if (decodeOptions->content.data != NULL && 1.241 + !NSS_CMSSignedData_HasDigests(sigd)) { 1.242 + PLArenaPool *poolp; 1.243 + SECAlgorithmID **digestalgs; 1.244 + 1.245 + /* detached content: grab content file */ 1.246 + sitem = decodeOptions->content; 1.247 + 1.248 + if ((poolp = PORT_NewArena(1024)) == NULL) { 1.249 + fprintf(stderr, "cmsutil: Out of memory.\n"); 1.250 + goto loser; 1.251 + } 1.252 + digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd); 1.253 + if (DigestFile (poolp, &digests, &sitem, digestalgs) 1.254 + != SECSuccess) { 1.255 + SECU_PrintError(progName, 1.256 + "problem computing message digest"); 1.257 + PORT_FreeArena(poolp, PR_FALSE); 1.258 + goto loser; 1.259 + } 1.260 + if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) 1.261 + != SECSuccess) { 1.262 + SECU_PrintError(progName, 1.263 + "problem setting message digests"); 1.264 + PORT_FreeArena(poolp, PR_FALSE); 1.265 + goto loser; 1.266 + } 1.267 + PORT_FreeArena(poolp, PR_FALSE); 1.268 + } 1.269 + 1.270 + /* import the certificates */ 1.271 + if (NSS_CMSSignedData_ImportCerts(sigd, 1.272 + decodeOptions->options->certHandle, 1.273 + decodeOptions->options->certUsage, 1.274 + decodeOptions->keepCerts) 1.275 + != SECSuccess) { 1.276 + SECU_PrintError(progName, "cert import failed"); 1.277 + goto loser; 1.278 + } 1.279 + 1.280 + /* find out about signers */ 1.281 + nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); 1.282 + if (decodeOptions->headerLevel >= 0) 1.283 + fprintf(out, "nsigners=%d; ", nsigners); 1.284 + if (nsigners == 0) { 1.285 + /* Might be a cert transport message 1.286 + ** or might be an invalid message, such as a QA test message 1.287 + ** or a message from an attacker. 1.288 + */ 1.289 + SECStatus rv; 1.290 + rv = NSS_CMSSignedData_VerifyCertsOnly(sigd, 1.291 + decodeOptions->options->certHandle, 1.292 + decodeOptions->options->certUsage); 1.293 + if (rv != SECSuccess) { 1.294 + fprintf(stderr, "cmsutil: Verify certs-only failed!\n"); 1.295 + goto loser; 1.296 + } 1.297 + return cmsg; 1.298 + } 1.299 + 1.300 + /* still no digests? */ 1.301 + if (!NSS_CMSSignedData_HasDigests(sigd)) { 1.302 + SECU_PrintError(progName, "no message digests"); 1.303 + goto loser; 1.304 + } 1.305 + 1.306 + for (j = 0; j < nsigners; j++) { 1.307 + const char * svs; 1.308 + NSSCMSSignerInfo *si; 1.309 + NSSCMSVerificationStatus vs; 1.310 + SECStatus bad; 1.311 + 1.312 + si = NSS_CMSSignedData_GetSignerInfo(sigd, j); 1.313 + if (decodeOptions->headerLevel >= 0) { 1.314 + char *signercn; 1.315 + static char empty[] = { "" }; 1.316 + 1.317 + signercn = NSS_CMSSignerInfo_GetSignerCommonName(si); 1.318 + if (signercn == NULL) 1.319 + signercn = empty; 1.320 + fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn); 1.321 + if (signercn != empty) 1.322 + PORT_Free(signercn); 1.323 + } 1.324 + bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j, 1.325 + decodeOptions->options->certHandle, 1.326 + decodeOptions->options->certUsage); 1.327 + vs = NSS_CMSSignerInfo_GetVerificationStatus(si); 1.328 + svs = NSS_CMSUtil_VerificationStatusToString(vs); 1.329 + if (decodeOptions->headerLevel >= 0) { 1.330 + fprintf(out, "signer%d.status=%s; ", j, svs); 1.331 + /* goto loser ? */ 1.332 + } else if (bad && out) { 1.333 + fprintf(stderr, "signer %d status = %s\n", j, svs); 1.334 + goto loser; 1.335 + } 1.336 + } 1.337 + } 1.338 + break; 1.339 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.340 + { 1.341 + NSSCMSEnvelopedData *envd; 1.342 + if (decodeOptions->headerLevel >= 0) 1.343 + fprintf(out, "type=envelopedData; "); 1.344 + envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo); 1.345 + if (envd == NULL) { 1.346 + SECU_PrintError(progName, "envelopedData component missing"); 1.347 + goto loser; 1.348 + } 1.349 + } 1.350 + break; 1.351 + case SEC_OID_PKCS7_ENCRYPTED_DATA: 1.352 + { 1.353 + NSSCMSEncryptedData *encd; 1.354 + if (decodeOptions->headerLevel >= 0) 1.355 + fprintf(out, "type=encryptedData; "); 1.356 + encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo); 1.357 + if (encd == NULL) { 1.358 + SECU_PrintError(progName, "encryptedData component missing"); 1.359 + goto loser; 1.360 + } 1.361 + } 1.362 + break; 1.363 + case SEC_OID_PKCS7_DATA: 1.364 + if (decodeOptions->headerLevel >= 0) 1.365 + fprintf(out, "type=data; "); 1.366 + break; 1.367 + default: 1.368 + break; 1.369 + } 1.370 + if (decodeOptions->headerLevel >= 0) 1.371 + fprintf(out, "\n"); 1.372 + } 1.373 + 1.374 + if (!decodeOptions->suppressContent && out) { 1.375 + SECItem *item = (sitem.data ? &sitem 1.376 + : NSS_CMSMessage_GetContent(cmsg)); 1.377 + if (item && item->data && item->len) { 1.378 + fwrite(item->data, item->len, 1, out); 1.379 + } 1.380 + } 1.381 + return cmsg; 1.382 + 1.383 +loser: 1.384 + if (cmsg) 1.385 + NSS_CMSMessage_Destroy(cmsg); 1.386 + return NULL; 1.387 +} 1.388 + 1.389 +/* example of a callback function to use with encoder */ 1.390 +/* 1.391 +static void 1.392 +writeout(void *arg, const char *buf, unsigned long len) 1.393 +{ 1.394 + FILE *f = (FILE *)arg; 1.395 + 1.396 + if (f != NULL && buf != NULL) 1.397 + (void)fwrite(buf, len, 1, f); 1.398 +} 1.399 +*/ 1.400 + 1.401 +static NSSCMSMessage * 1.402 +signed_data(struct signOptionsStr *signOptions) 1.403 +{ 1.404 + NSSCMSMessage *cmsg = NULL; 1.405 + NSSCMSContentInfo *cinfo; 1.406 + NSSCMSSignedData *sigd; 1.407 + NSSCMSSignerInfo *signerinfo; 1.408 + CERTCertificate *cert= NULL, *ekpcert = NULL; 1.409 + 1.410 + if (cms_verbose) { 1.411 + fprintf(stderr, "Input to signed_data:\n"); 1.412 + if (signOptions->options->password) 1.413 + fprintf(stderr, "password [%s]\n", signOptions->options->password); 1.414 + else if (signOptions->options->pwfile) 1.415 + fprintf(stderr, "password file [%s]\n", signOptions->options->pwfile); 1.416 + else 1.417 + fprintf(stderr, "password [NULL]\n"); 1.418 + fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage); 1.419 + if (signOptions->options->certHandle) 1.420 + fprintf(stderr, "certdb [%p]\n", signOptions->options->certHandle); 1.421 + else 1.422 + fprintf(stderr, "certdb [NULL]\n"); 1.423 + if (signOptions->nickname) 1.424 + fprintf(stderr, "nickname [%s]\n", signOptions->nickname); 1.425 + else 1.426 + fprintf(stderr, "nickname [NULL]\n"); 1.427 + } 1.428 + if (signOptions->nickname == NULL) { 1.429 + fprintf(stderr, 1.430 + "ERROR: please indicate the nickname of a certificate to sign with.\n"); 1.431 + return NULL; 1.432 + } 1.433 + if ((cert = CERT_FindUserCertByUsage(signOptions->options->certHandle, 1.434 + signOptions->nickname, 1.435 + signOptions->options->certUsage, 1.436 + PR_FALSE, 1.437 + &pwdata)) == NULL) { 1.438 + SECU_PrintError(progName, 1.439 + "the corresponding cert for key \"%s\" does not exist", 1.440 + signOptions->nickname); 1.441 + return NULL; 1.442 + } 1.443 + if (cms_verbose) { 1.444 + fprintf(stderr, "Found certificate for %s\n", signOptions->nickname); 1.445 + } 1.446 + /* 1.447 + * create the message object 1.448 + */ 1.449 + cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ 1.450 + if (cmsg == NULL) { 1.451 + fprintf(stderr, "ERROR: cannot create CMS message.\n"); 1.452 + return NULL; 1.453 + } 1.454 + /* 1.455 + * build chain of objects: message->signedData->data 1.456 + */ 1.457 + if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) { 1.458 + fprintf(stderr, "ERROR: cannot create CMS signedData object.\n"); 1.459 + goto loser; 1.460 + } 1.461 + cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 1.462 + if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) 1.463 + != SECSuccess) { 1.464 + fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n"); 1.465 + goto loser; 1.466 + } 1.467 + cinfo = NSS_CMSSignedData_GetContentInfo(sigd); 1.468 + /* we're always passing data in and detaching optionally */ 1.469 + if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, 1.470 + signOptions->detached) 1.471 + != SECSuccess) { 1.472 + fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 1.473 + goto loser; 1.474 + } 1.475 + /* 1.476 + * create & attach signer information 1.477 + */ 1.478 + signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, signOptions->hashAlgTag); 1.479 + if (signerinfo == NULL) { 1.480 + fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n"); 1.481 + goto loser; 1.482 + } 1.483 + if (cms_verbose) { 1.484 + fprintf(stderr, 1.485 + "Created CMS message, added signed data w/ signerinfo\n"); 1.486 + } 1.487 + /* we want the cert chain included for this one */ 1.488 + if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, 1.489 + signOptions->options->certUsage) 1.490 + != SECSuccess) { 1.491 + fprintf(stderr, "ERROR: cannot find cert chain.\n"); 1.492 + goto loser; 1.493 + } 1.494 + if (cms_verbose) { 1.495 + fprintf(stderr, "imported certificate\n"); 1.496 + } 1.497 + if (signOptions->signingTime) { 1.498 + if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) 1.499 + != SECSuccess) { 1.500 + fprintf(stderr, "ERROR: cannot add signingTime attribute.\n"); 1.501 + goto loser; 1.502 + } 1.503 + } 1.504 + if (signOptions->smimeProfile) { 1.505 + if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { 1.506 + fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n"); 1.507 + goto loser; 1.508 + } 1.509 + } 1.510 + 1.511 + if (!signOptions->encryptionKeyPreferenceNick) { 1.512 + /* check signing cert for fitness as encryption cert */ 1.513 + SECStatus FitForEncrypt = CERT_CheckCertUsage(cert, 1.514 + certUsageEmailRecipient); 1.515 + 1.516 + if (SECSuccess == FitForEncrypt) { 1.517 + /* if yes, add signing cert as EncryptionKeyPreference */ 1.518 + if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert, 1.519 + signOptions->options->certHandle) 1.520 + != SECSuccess) { 1.521 + fprintf(stderr, 1.522 + "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n"); 1.523 + goto loser; 1.524 + } 1.525 + if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, cert, 1.526 + signOptions->options->certHandle) 1.527 + != SECSuccess) { 1.528 + fprintf(stderr, 1.529 + "ERROR: cannot add default MS SMIMEEncKeyPrefs attribute.\n"); 1.530 + goto loser; 1.531 + } 1.532 + } else { 1.533 + /* this is a dual-key cert case, we need to look for the encryption 1.534 + certificate under the same nickname as the signing cert */ 1.535 + /* get the cert, add it to the message */ 1.536 + if ((ekpcert = CERT_FindUserCertByUsage( 1.537 + signOptions->options->certHandle, 1.538 + signOptions->nickname, 1.539 + certUsageEmailRecipient, 1.540 + PR_FALSE, 1.541 + &pwdata)) == NULL) { 1.542 + SECU_PrintError(progName, 1.543 + "the corresponding cert for key \"%s\" does not exist", 1.544 + signOptions->encryptionKeyPreferenceNick); 1.545 + goto loser; 1.546 + } 1.547 + if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, 1.548 + signOptions->options->certHandle) 1.549 + != SECSuccess) { 1.550 + fprintf(stderr, 1.551 + "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n"); 1.552 + goto loser; 1.553 + } 1.554 + if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, 1.555 + signOptions->options->certHandle) 1.556 + != SECSuccess) { 1.557 + fprintf(stderr, 1.558 + "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n"); 1.559 + goto loser; 1.560 + } 1.561 + if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) { 1.562 + fprintf(stderr, "ERROR: cannot add encryption certificate.\n"); 1.563 + goto loser; 1.564 + } 1.565 + } 1.566 + } else if (PL_strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0) { 1.567 + /* No action */ 1.568 + } else { 1.569 + /* get the cert, add it to the message */ 1.570 + if ((ekpcert = CERT_FindUserCertByUsage( 1.571 + signOptions->options->certHandle, 1.572 + signOptions->encryptionKeyPreferenceNick, 1.573 + certUsageEmailRecipient, PR_FALSE, &pwdata)) 1.574 + == NULL) { 1.575 + SECU_PrintError(progName, 1.576 + "the corresponding cert for key \"%s\" does not exist", 1.577 + signOptions->encryptionKeyPreferenceNick); 1.578 + goto loser; 1.579 + } 1.580 + if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, 1.581 + signOptions->options->certHandle) 1.582 + != SECSuccess) { 1.583 + fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n"); 1.584 + goto loser; 1.585 + } 1.586 + if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, 1.587 + signOptions->options->certHandle) 1.588 + != SECSuccess) { 1.589 + fprintf(stderr, "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n"); 1.590 + goto loser; 1.591 + } 1.592 + if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) { 1.593 + fprintf(stderr, "ERROR: cannot add encryption certificate.\n"); 1.594 + goto loser; 1.595 + } 1.596 + } 1.597 + 1.598 + if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { 1.599 + fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n"); 1.600 + goto loser; 1.601 + } 1.602 + if (cms_verbose) { 1.603 + fprintf(stderr, "created signed-data message\n"); 1.604 + } 1.605 + if (ekpcert) { 1.606 + CERT_DestroyCertificate(ekpcert); 1.607 + } 1.608 + if (cert) { 1.609 + CERT_DestroyCertificate(cert); 1.610 + } 1.611 + return cmsg; 1.612 +loser: 1.613 + if (ekpcert) { 1.614 + CERT_DestroyCertificate(ekpcert); 1.615 + } 1.616 + if (cert) { 1.617 + CERT_DestroyCertificate(cert); 1.618 + } 1.619 + NSS_CMSMessage_Destroy(cmsg); 1.620 + return NULL; 1.621 +} 1.622 + 1.623 +static NSSCMSMessage * 1.624 +enveloped_data(struct envelopeOptionsStr *envelopeOptions) 1.625 +{ 1.626 + NSSCMSMessage *cmsg = NULL; 1.627 + NSSCMSContentInfo *cinfo; 1.628 + NSSCMSEnvelopedData *envd; 1.629 + NSSCMSRecipientInfo *recipientinfo; 1.630 + CERTCertificate **recipientcerts = NULL; 1.631 + CERTCertDBHandle *dbhandle; 1.632 + PLArenaPool *tmppoolp = NULL; 1.633 + SECOidTag bulkalgtag; 1.634 + int keysize, i = 0; 1.635 + int cnt; 1.636 + dbhandle = envelopeOptions->options->certHandle; 1.637 + /* count the recipients */ 1.638 + if ((cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients)) == 0) { 1.639 + fprintf(stderr, "ERROR: please name at least one recipient.\n"); 1.640 + goto loser; 1.641 + } 1.642 + if ((tmppoolp = PORT_NewArena (1024)) == NULL) { 1.643 + fprintf(stderr, "ERROR: out of memory.\n"); 1.644 + goto loser; 1.645 + } 1.646 + /* XXX find the recipient's certs by email address or nickname */ 1.647 + if ((recipientcerts = 1.648 + (CERTCertificate **)PORT_ArenaZAlloc(tmppoolp, 1.649 + (cnt+1)*sizeof(CERTCertificate*))) 1.650 + == NULL) { 1.651 + fprintf(stderr, "ERROR: out of memory.\n"); 1.652 + goto loser; 1.653 + } 1.654 + for (i=0; envelopeOptions->recipients[i] != NULL; i++) { 1.655 + if ((recipientcerts[i] = 1.656 + CERT_FindCertByNicknameOrEmailAddr(dbhandle, 1.657 + envelopeOptions->recipients[i])) 1.658 + == NULL) { 1.659 + SECU_PrintError(progName, "cannot find certificate for \"%s\"", 1.660 + envelopeOptions->recipients[i]); 1.661 + i=0; 1.662 + goto loser; 1.663 + } 1.664 + } 1.665 + recipientcerts[i] = NULL; 1.666 + i=0; 1.667 + /* find a nice bulk algorithm */ 1.668 + if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag, 1.669 + &keysize) != SECSuccess) { 1.670 + fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n"); 1.671 + goto loser; 1.672 + } 1.673 + /* 1.674 + * create the message object 1.675 + */ 1.676 + cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ 1.677 + if (cmsg == NULL) { 1.678 + fprintf(stderr, "ERROR: cannot create CMS message.\n"); 1.679 + goto loser; 1.680 + } 1.681 + /* 1.682 + * build chain of objects: message->envelopedData->data 1.683 + */ 1.684 + if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize)) 1.685 + == NULL) { 1.686 + fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n"); 1.687 + goto loser; 1.688 + } 1.689 + cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 1.690 + if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) 1.691 + != SECSuccess) { 1.692 + fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n"); 1.693 + goto loser; 1.694 + } 1.695 + cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); 1.696 + /* we're always passing data in, so the content is NULL */ 1.697 + if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) 1.698 + != SECSuccess) { 1.699 + fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 1.700 + goto loser; 1.701 + } 1.702 + /* 1.703 + * create & attach recipient information 1.704 + */ 1.705 + for (i = 0; recipientcerts[i] != NULL; i++) { 1.706 + if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg, 1.707 + recipientcerts[i])) 1.708 + == NULL) { 1.709 + fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n"); 1.710 + goto loser; 1.711 + } 1.712 + if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo) 1.713 + != SECSuccess) { 1.714 + fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n"); 1.715 + goto loser; 1.716 + } 1.717 + CERT_DestroyCertificate(recipientcerts[i]); 1.718 + } 1.719 + if (tmppoolp) 1.720 + PORT_FreeArena(tmppoolp, PR_FALSE); 1.721 + return cmsg; 1.722 +loser: 1.723 + if (recipientcerts) { 1.724 + for (; recipientcerts[i] != NULL; i++) { 1.725 + CERT_DestroyCertificate(recipientcerts[i]); 1.726 + } 1.727 + } 1.728 + if (cmsg) 1.729 + NSS_CMSMessage_Destroy(cmsg); 1.730 + if (tmppoolp) 1.731 + PORT_FreeArena(tmppoolp, PR_FALSE); 1.732 + return NULL; 1.733 +} 1.734 + 1.735 +PK11SymKey *dkcb(void *arg, SECAlgorithmID *algid) 1.736 +{ 1.737 + return (PK11SymKey*)arg; 1.738 +} 1.739 + 1.740 +static SECStatus 1.741 +get_enc_params(struct encryptOptionsStr *encryptOptions) 1.742 +{ 1.743 + struct envelopeOptionsStr envelopeOptions; 1.744 + SECStatus rv = SECFailure; 1.745 + NSSCMSMessage *env_cmsg; 1.746 + NSSCMSContentInfo *cinfo; 1.747 + int i, nlevels; 1.748 + /* 1.749 + * construct an enveloped data message to obtain bulk keys 1.750 + */ 1.751 + if (encryptOptions->envmsg) { 1.752 + env_cmsg = encryptOptions->envmsg; /* get it from an old message */ 1.753 + } else { 1.754 + SECItem dummyOut = { 0, 0, 0 }; 1.755 + SECItem dummyIn = { 0, 0, 0 }; 1.756 + char str[] = "Hello!"; 1.757 + PLArenaPool *tmparena = PORT_NewArena(1024); 1.758 + dummyIn.data = (unsigned char *)str; 1.759 + dummyIn.len = strlen(str); 1.760 + envelopeOptions.options = encryptOptions->options; 1.761 + envelopeOptions.recipients = encryptOptions->recipients; 1.762 + env_cmsg = enveloped_data(&envelopeOptions); 1.763 + NSS_CMSDEREncode(env_cmsg, &dummyIn, &dummyOut, tmparena); 1.764 + PR_Write(encryptOptions->envFile, dummyOut.data, dummyOut.len); 1.765 + PORT_FreeArena(tmparena, PR_FALSE); 1.766 + } 1.767 + /* 1.768 + * get the content info for the enveloped data 1.769 + */ 1.770 + nlevels = NSS_CMSMessage_ContentLevelCount(env_cmsg); 1.771 + for (i = 0; i < nlevels; i++) { 1.772 + SECOidTag typetag; 1.773 + cinfo = NSS_CMSMessage_ContentLevel(env_cmsg, i); 1.774 + typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 1.775 + if (typetag == SEC_OID_PKCS7_DATA) { 1.776 + /* 1.777 + * get the symmetric key 1.778 + */ 1.779 + encryptOptions->bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo); 1.780 + encryptOptions->keysize = NSS_CMSContentInfo_GetBulkKeySize(cinfo); 1.781 + encryptOptions->bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); 1.782 + rv = SECSuccess; 1.783 + break; 1.784 + } 1.785 + } 1.786 + if (i == nlevels) { 1.787 + fprintf(stderr, "%s: could not retrieve enveloped data.", progName); 1.788 + } 1.789 + if (env_cmsg) 1.790 + NSS_CMSMessage_Destroy(env_cmsg); 1.791 + return rv; 1.792 +} 1.793 + 1.794 +static NSSCMSMessage * 1.795 +encrypted_data(struct encryptOptionsStr *encryptOptions) 1.796 +{ 1.797 + SECStatus rv = SECFailure; 1.798 + NSSCMSMessage *cmsg = NULL; 1.799 + NSSCMSContentInfo *cinfo; 1.800 + NSSCMSEncryptedData *encd; 1.801 + NSSCMSEncoderContext *ecx = NULL; 1.802 + PLArenaPool *tmppoolp = NULL; 1.803 + SECItem derOut = { 0, 0, 0 }; 1.804 + /* arena for output */ 1.805 + tmppoolp = PORT_NewArena(1024); 1.806 + if (!tmppoolp) { 1.807 + fprintf(stderr, "%s: out of memory.\n", progName); 1.808 + return NULL; 1.809 + } 1.810 + /* 1.811 + * create the message object 1.812 + */ 1.813 + cmsg = NSS_CMSMessage_Create(NULL); 1.814 + if (cmsg == NULL) { 1.815 + fprintf(stderr, "ERROR: cannot create CMS message.\n"); 1.816 + goto loser; 1.817 + } 1.818 + /* 1.819 + * build chain of objects: message->encryptedData->data 1.820 + */ 1.821 + if ((encd = NSS_CMSEncryptedData_Create(cmsg, encryptOptions->bulkalgtag, 1.822 + encryptOptions->keysize)) 1.823 + == NULL) { 1.824 + fprintf(stderr, "ERROR: cannot create CMS encryptedData object.\n"); 1.825 + goto loser; 1.826 + } 1.827 + cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 1.828 + if (NSS_CMSContentInfo_SetContent_EncryptedData(cmsg, cinfo, encd) 1.829 + != SECSuccess) { 1.830 + fprintf(stderr, "ERROR: cannot attach CMS encryptedData object.\n"); 1.831 + goto loser; 1.832 + } 1.833 + cinfo = NSS_CMSEncryptedData_GetContentInfo(encd); 1.834 + /* we're always passing data in, so the content is NULL */ 1.835 + if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) 1.836 + != SECSuccess) { 1.837 + fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 1.838 + goto loser; 1.839 + } 1.840 + ecx = NSS_CMSEncoder_Start(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL, 1.841 + dkcb, encryptOptions->bulkkey, NULL, NULL); 1.842 + if (!ecx) { 1.843 + fprintf(stderr, "%s: cannot create encoder context.\n", progName); 1.844 + goto loser; 1.845 + } 1.846 + rv = NSS_CMSEncoder_Update(ecx, (char *)encryptOptions->input->data, 1.847 + encryptOptions->input->len); 1.848 + if (rv) { 1.849 + fprintf(stderr, "%s: failed to add data to encoder.\n", progName); 1.850 + goto loser; 1.851 + } 1.852 + rv = NSS_CMSEncoder_Finish(ecx); 1.853 + if (rv) { 1.854 + fprintf(stderr, "%s: failed to encrypt data.\n", progName); 1.855 + goto loser; 1.856 + } 1.857 + fwrite(derOut.data, derOut.len, 1, encryptOptions->outfile); 1.858 + /* 1.859 + if (bulkkey) 1.860 + PK11_FreeSymKey(bulkkey); 1.861 + */ 1.862 + if (tmppoolp) 1.863 + PORT_FreeArena(tmppoolp, PR_FALSE); 1.864 + return cmsg; 1.865 +loser: 1.866 + /* 1.867 + if (bulkkey) 1.868 + PK11_FreeSymKey(bulkkey); 1.869 + */ 1.870 + if (tmppoolp) 1.871 + PORT_FreeArena(tmppoolp, PR_FALSE); 1.872 + if (cmsg) 1.873 + NSS_CMSMessage_Destroy(cmsg); 1.874 + return NULL; 1.875 +} 1.876 + 1.877 +static NSSCMSMessage * 1.878 +signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions) 1.879 +{ 1.880 + NSSCMSMessage *cmsg = NULL; 1.881 + NSSCMSContentInfo *cinfo; 1.882 + NSSCMSSignedData *sigd; 1.883 + CERTCertificate **certs = NULL; 1.884 + CERTCertDBHandle *dbhandle; 1.885 + PLArenaPool *tmppoolp = NULL; 1.886 + int i = 0, cnt; 1.887 + dbhandle = certsonlyOptions->options->certHandle; 1.888 + if ((cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients)) == 0) { 1.889 + fprintf(stderr, 1.890 + "ERROR: please indicate the nickname of a certificate to sign with.\n"); 1.891 + goto loser; 1.892 + } 1.893 + if (!(tmppoolp = PORT_NewArena(1024))) { 1.894 + fprintf(stderr, "ERROR: out of memory.\n"); 1.895 + goto loser; 1.896 + } 1.897 + if (!(certs = PORT_ArenaZNewArray(tmppoolp, CERTCertificate *, cnt + 1))) { 1.898 + fprintf(stderr, "ERROR: out of memory.\n"); 1.899 + goto loser; 1.900 + } 1.901 + for (i=0; certsonlyOptions->recipients[i] != NULL; i++) { 1.902 + if ((certs[i] = 1.903 + CERT_FindCertByNicknameOrEmailAddr(dbhandle, 1.904 + certsonlyOptions->recipients[i])) 1.905 + == NULL) { 1.906 + SECU_PrintError(progName, "cannot find certificate for \"%s\"", 1.907 + certsonlyOptions->recipients[i]); 1.908 + i=0; 1.909 + goto loser; 1.910 + } 1.911 + } 1.912 + certs[i] = NULL; 1.913 + i=0; 1.914 + /* 1.915 + * create the message object 1.916 + */ 1.917 + cmsg = NSS_CMSMessage_Create(NULL); 1.918 + if (cmsg == NULL) { 1.919 + fprintf(stderr, "ERROR: cannot create CMS message.\n"); 1.920 + goto loser; 1.921 + } 1.922 + /* 1.923 + * build chain of objects: message->signedData->data 1.924 + */ 1.925 + if ((sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, certs[0], PR_TRUE)) 1.926 + == NULL) { 1.927 + fprintf(stderr, "ERROR: cannot create CMS signedData object.\n"); 1.928 + goto loser; 1.929 + } 1.930 + CERT_DestroyCertificate(certs[0]); 1.931 + for (i=1; i<cnt; i++) { 1.932 + if (NSS_CMSSignedData_AddCertChain(sigd, certs[i])) { 1.933 + fprintf(stderr, "ERROR: cannot add cert chain for \"%s\".\n", 1.934 + certsonlyOptions->recipients[i]); 1.935 + goto loser; 1.936 + } 1.937 + CERT_DestroyCertificate(certs[i]); 1.938 + } 1.939 + cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 1.940 + if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) 1.941 + != SECSuccess) { 1.942 + fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n"); 1.943 + goto loser; 1.944 + } 1.945 + cinfo = NSS_CMSSignedData_GetContentInfo(sigd); 1.946 + if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) 1.947 + != SECSuccess) { 1.948 + fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 1.949 + goto loser; 1.950 + } 1.951 + if (tmppoolp) 1.952 + PORT_FreeArena(tmppoolp, PR_FALSE); 1.953 + return cmsg; 1.954 +loser: 1.955 + if (certs) { 1.956 + for (; i<cnt; i++) { 1.957 + CERT_DestroyCertificate(certs[i]); 1.958 + } 1.959 + } 1.960 + if (cmsg) 1.961 + NSS_CMSMessage_Destroy(cmsg); 1.962 + if (tmppoolp) 1.963 + PORT_FreeArena(tmppoolp, PR_FALSE); 1.964 + return NULL; 1.965 +} 1.966 + 1.967 +static char * 1.968 +pl_fgets(char * buf, int size, PRFileDesc * fd) 1.969 +{ 1.970 + char * bp = buf; 1.971 + int nb = 0;; 1.972 + 1.973 + while (size > 1) { 1.974 + nb = PR_Read(fd, bp, 1); 1.975 + if (nb < 0) { 1.976 + /* deal with error */ 1.977 + return NULL; 1.978 + } else if (nb == 0) { 1.979 + /* deal with EOF */ 1.980 + return NULL; 1.981 + } else if (*bp == '\n') { 1.982 + /* deal with EOL */ 1.983 + ++bp; /* keep EOL character */ 1.984 + break; 1.985 + } else { 1.986 + /* ordinary character */ 1.987 + ++bp; 1.988 + --size; 1.989 + } 1.990 + } 1.991 + *bp = '\0'; 1.992 + return buf; 1.993 +} 1.994 + 1.995 +typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode; 1.996 + 1.997 +static int 1.998 +doBatchDecode(FILE *outFile, PRFileDesc *batchFile, 1.999 + const struct decodeOptionsStr *decodeOptions) 1.1000 +{ 1.1001 + char * str; 1.1002 + int exitStatus = 0; 1.1003 + char batchLine[512]; 1.1004 + 1.1005 + while (NULL != (str = pl_fgets(batchLine, sizeof batchLine, batchFile))) { 1.1006 + NSSCMSMessage *cmsg = NULL; 1.1007 + PRFileDesc * inFile; 1.1008 + int len = strlen(str); 1.1009 + SECStatus rv; 1.1010 + SECItem input = {0, 0, 0}; 1.1011 + char cc; 1.1012 + 1.1013 + while (len > 0 && 1.1014 + ((cc = str[len - 1]) == '\n' || cc == '\r')) { 1.1015 + str[--len] = '\0'; 1.1016 + } 1.1017 + if (!len) /* skip empty line */ 1.1018 + continue; 1.1019 + if (str[0] == '#') 1.1020 + continue; /* skip comment line */ 1.1021 + fprintf(outFile, "========== %s ==========\n", str); 1.1022 + inFile = PR_Open(str, PR_RDONLY, 00660); 1.1023 + if (inFile == NULL) { 1.1024 + fprintf(outFile, "%s: unable to open \"%s\" for reading\n", 1.1025 + progName, str); 1.1026 + exitStatus = 1; 1.1027 + continue; 1.1028 + } 1.1029 + rv = SECU_FileToItem(&input, inFile); 1.1030 + PR_Close(inFile); 1.1031 + if (rv != SECSuccess) { 1.1032 + SECU_PrintError(progName, "unable to read infile"); 1.1033 + exitStatus = 1; 1.1034 + continue; 1.1035 + } 1.1036 + cmsg = decode(outFile, &input, decodeOptions); 1.1037 + SECITEM_FreeItem(&input, PR_FALSE); 1.1038 + if (cmsg) 1.1039 + NSS_CMSMessage_Destroy(cmsg); 1.1040 + else { 1.1041 + SECU_PrintError(progName, "problem decoding"); 1.1042 + exitStatus = 1; 1.1043 + } 1.1044 + } 1.1045 + return exitStatus; 1.1046 +} 1.1047 + 1.1048 +int 1.1049 +main(int argc, char **argv) 1.1050 +{ 1.1051 + FILE *outFile; 1.1052 + NSSCMSMessage *cmsg = NULL; 1.1053 + PRFileDesc *inFile; 1.1054 + PLOptState *optstate; 1.1055 + PLOptStatus status; 1.1056 + Mode mode = UNKNOWN; 1.1057 + struct decodeOptionsStr decodeOptions = { 0 }; 1.1058 + struct signOptionsStr signOptions = { 0 }; 1.1059 + struct envelopeOptionsStr envelopeOptions = { 0 }; 1.1060 + struct certsonlyOptionsStr certsonlyOptions = { 0 }; 1.1061 + struct encryptOptionsStr encryptOptions = { 0 }; 1.1062 + struct optionsStr options = { 0 }; 1.1063 + int exitstatus; 1.1064 + static char *ptrarray[128] = { 0 }; 1.1065 + int nrecipients = 0; 1.1066 + char *str, *tok; 1.1067 + char *envFileName; 1.1068 + SECItem input = { 0, 0, 0}; 1.1069 + SECItem envmsg = { 0, 0, 0 }; 1.1070 + SECStatus rv; 1.1071 + PRFileDesc *contentFile = NULL; 1.1072 + PRBool batch = PR_FALSE; 1.1073 + 1.1074 +#ifdef NISCC_TEST 1.1075 + const char *ev = PR_GetEnv("NSS_DISABLE_ARENA_FREE_LIST"); 1.1076 + PORT_Assert(ev); 1.1077 + ev = PR_GetEnv("NSS_STRICT_SHUTDOWN"); 1.1078 + PORT_Assert(ev); 1.1079 +#endif 1.1080 + 1.1081 + progName = strrchr(argv[0], '/'); 1.1082 + if (!progName) 1.1083 + progName = strrchr(argv[0], '\\'); 1.1084 + progName = progName ? progName+1 : argv[0]; 1.1085 + 1.1086 + inFile = PR_STDIN; 1.1087 + outFile = stdout; 1.1088 + envFileName = NULL; 1.1089 + mode = UNKNOWN; 1.1090 + decodeOptions.content.data = NULL; 1.1091 + decodeOptions.content.len = 0; 1.1092 + decodeOptions.suppressContent = PR_FALSE; 1.1093 + decodeOptions.headerLevel = -1; 1.1094 + decodeOptions.keepCerts = PR_FALSE; 1.1095 + options.certUsage = certUsageEmailSigner; 1.1096 + options.password = NULL; 1.1097 + options.pwfile = NULL; 1.1098 + signOptions.nickname = NULL; 1.1099 + signOptions.detached = PR_FALSE; 1.1100 + signOptions.signingTime = PR_FALSE; 1.1101 + signOptions.smimeProfile = PR_FALSE; 1.1102 + signOptions.encryptionKeyPreferenceNick = NULL; 1.1103 + signOptions.hashAlgTag = SEC_OID_SHA1; 1.1104 + envelopeOptions.recipients = NULL; 1.1105 + encryptOptions.recipients = NULL; 1.1106 + encryptOptions.envmsg = NULL; 1.1107 + encryptOptions.envFile = NULL; 1.1108 + encryptOptions.bulkalgtag = SEC_OID_UNKNOWN; 1.1109 + encryptOptions.bulkkey = NULL; 1.1110 + encryptOptions.keysize = -1; 1.1111 + 1.1112 + /* 1.1113 + * Parse command line arguments 1.1114 + */ 1.1115 + optstate = PL_CreateOptState(argc, argv, 1.1116 + "CDEGH:N:OPSTY:bc:d:e:f:h:i:kno:p:r:s:u:v"); 1.1117 + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 1.1118 + switch (optstate->option) { 1.1119 + case 'C': 1.1120 + mode = ENCRYPT; 1.1121 + break; 1.1122 + case 'D': 1.1123 + mode = DECODE; 1.1124 + break; 1.1125 + case 'E': 1.1126 + mode = ENVELOPE; 1.1127 + break; 1.1128 + case 'G': 1.1129 + if (mode != SIGN) { 1.1130 + fprintf(stderr, 1.1131 + "%s: option -G only supported with option -S.\n", 1.1132 + progName); 1.1133 + Usage(progName); 1.1134 + exit(1); 1.1135 + } 1.1136 + signOptions.signingTime = PR_TRUE; 1.1137 + break; 1.1138 + case 'H': 1.1139 + if (mode != SIGN) { 1.1140 + fprintf(stderr, 1.1141 + "%s: option -H only supported with option -S.\n", 1.1142 + progName); 1.1143 + Usage(progName); 1.1144 + exit(1); 1.1145 + } 1.1146 + decodeOptions.suppressContent = PR_TRUE; 1.1147 + if (!strcmp(optstate->value, "MD2")) 1.1148 + signOptions.hashAlgTag = SEC_OID_MD2; 1.1149 + else if (!strcmp(optstate->value, "MD4")) 1.1150 + signOptions.hashAlgTag = SEC_OID_MD4; 1.1151 + else if (!strcmp(optstate->value, "MD5")) 1.1152 + signOptions.hashAlgTag = SEC_OID_MD5; 1.1153 + else if (!strcmp(optstate->value, "SHA1")) 1.1154 + signOptions.hashAlgTag = SEC_OID_SHA1; 1.1155 + else if (!strcmp(optstate->value, "SHA256")) 1.1156 + signOptions.hashAlgTag = SEC_OID_SHA256; 1.1157 + else if (!strcmp(optstate->value, "SHA384")) 1.1158 + signOptions.hashAlgTag = SEC_OID_SHA384; 1.1159 + else if (!strcmp(optstate->value, "SHA512")) 1.1160 + signOptions.hashAlgTag = SEC_OID_SHA512; 1.1161 + else { 1.1162 + fprintf(stderr, 1.1163 + "%s: -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512\n", 1.1164 + progName); 1.1165 + exit(1); 1.1166 + } 1.1167 + break; 1.1168 + case 'N': 1.1169 + if (mode != SIGN) { 1.1170 + fprintf(stderr, 1.1171 + "%s: option -N only supported with option -S.\n", 1.1172 + progName); 1.1173 + Usage(progName); 1.1174 + exit(1); 1.1175 + } 1.1176 + signOptions.nickname = strdup(optstate->value); 1.1177 + break; 1.1178 + case 'O': 1.1179 + mode = CERTSONLY; 1.1180 + break; 1.1181 + case 'P': 1.1182 + if (mode != SIGN) { 1.1183 + fprintf(stderr, 1.1184 + "%s: option -P only supported with option -S.\n", 1.1185 + progName); 1.1186 + Usage(progName); 1.1187 + exit(1); 1.1188 + } 1.1189 + signOptions.smimeProfile = PR_TRUE; 1.1190 + break; 1.1191 + case 'S': 1.1192 + mode = SIGN; 1.1193 + break; 1.1194 + case 'T': 1.1195 + if (mode != SIGN) { 1.1196 + fprintf(stderr, 1.1197 + "%s: option -T only supported with option -S.\n", 1.1198 + progName); 1.1199 + Usage(progName); 1.1200 + exit(1); 1.1201 + } 1.1202 + signOptions.detached = PR_TRUE; 1.1203 + break; 1.1204 + case 'Y': 1.1205 + if (mode != SIGN) { 1.1206 + fprintf(stderr, 1.1207 + "%s: option -Y only supported with option -S.\n", 1.1208 + progName); 1.1209 + Usage(progName); 1.1210 + exit(1); 1.1211 + } 1.1212 + signOptions.encryptionKeyPreferenceNick = strdup(optstate->value); 1.1213 + break; 1.1214 + 1.1215 + case 'b': 1.1216 + if (mode != DECODE) { 1.1217 + fprintf(stderr, 1.1218 + "%s: option -b only supported with option -D.\n", 1.1219 + progName); 1.1220 + Usage(progName); 1.1221 + exit(1); 1.1222 + } 1.1223 + batch = PR_TRUE; 1.1224 + break; 1.1225 + 1.1226 + case 'c': 1.1227 + if (mode != DECODE) { 1.1228 + fprintf(stderr, 1.1229 + "%s: option -c only supported with option -D.\n", 1.1230 + progName); 1.1231 + Usage(progName); 1.1232 + exit(1); 1.1233 + } 1.1234 + contentFile = PR_Open(optstate->value, PR_RDONLY, 006600); 1.1235 + if (contentFile == NULL) { 1.1236 + fprintf(stderr, "%s: unable to open \"%s\" for reading.\n", 1.1237 + progName, optstate->value); 1.1238 + exit(1); 1.1239 + } 1.1240 + 1.1241 + rv = SECU_FileToItem(&decodeOptions.content, contentFile); 1.1242 + PR_Close(contentFile); 1.1243 + if (rv != SECSuccess) { 1.1244 + SECU_PrintError(progName, "problem reading content file"); 1.1245 + exit(1); 1.1246 + } 1.1247 + if (!decodeOptions.content.data) { 1.1248 + /* file was zero length */ 1.1249 + decodeOptions.content.data = (unsigned char *)PORT_Strdup(""); 1.1250 + decodeOptions.content.len = 0; 1.1251 + } 1.1252 + 1.1253 + break; 1.1254 + case 'd': 1.1255 + SECU_ConfigDirectory(optstate->value); 1.1256 + break; 1.1257 + case 'e': 1.1258 + envFileName = strdup(optstate->value); 1.1259 + encryptOptions.envFile = PR_Open(envFileName, PR_RDONLY, 00660); 1.1260 + break; 1.1261 + 1.1262 + case 'h': 1.1263 + if (mode != DECODE) { 1.1264 + fprintf(stderr, 1.1265 + "%s: option -h only supported with option -D.\n", 1.1266 + progName); 1.1267 + Usage(progName); 1.1268 + exit(1); 1.1269 + } 1.1270 + decodeOptions.headerLevel = atoi(optstate->value); 1.1271 + if (decodeOptions.headerLevel < 0) { 1.1272 + fprintf(stderr, "option -h cannot have a negative value.\n"); 1.1273 + exit(1); 1.1274 + } 1.1275 + break; 1.1276 + case 'i': 1.1277 + if (!optstate->value) { 1.1278 + fprintf(stderr, "-i option requires filename argument\n"); 1.1279 + exit(1); 1.1280 + } 1.1281 + inFile = PR_Open(optstate->value, PR_RDONLY, 00660); 1.1282 + if (inFile == NULL) { 1.1283 + fprintf(stderr, "%s: unable to open \"%s\" for reading\n", 1.1284 + progName, optstate->value); 1.1285 + exit(1); 1.1286 + } 1.1287 + break; 1.1288 + 1.1289 + case 'k': 1.1290 + if (mode != DECODE) { 1.1291 + fprintf(stderr, 1.1292 + "%s: option -k only supported with option -D.\n", 1.1293 + progName); 1.1294 + Usage(progName); 1.1295 + exit(1); 1.1296 + } 1.1297 + decodeOptions.keepCerts = PR_TRUE; 1.1298 + break; 1.1299 + 1.1300 + case 'n': 1.1301 + if (mode != DECODE) { 1.1302 + fprintf(stderr, 1.1303 + "%s: option -n only supported with option -D.\n", 1.1304 + progName); 1.1305 + Usage(progName); 1.1306 + exit(1); 1.1307 + } 1.1308 + decodeOptions.suppressContent = PR_TRUE; 1.1309 + break; 1.1310 + case 'o': 1.1311 + outFile = fopen(optstate->value, "wb"); 1.1312 + if (outFile == NULL) { 1.1313 + fprintf(stderr, "%s: unable to open \"%s\" for writing\n", 1.1314 + progName, optstate->value); 1.1315 + exit(1); 1.1316 + } 1.1317 + break; 1.1318 + case 'p': 1.1319 + if (!optstate->value) { 1.1320 + fprintf(stderr, "%s: option -p must have a value.\n", progName); 1.1321 + Usage(progName); 1.1322 + exit(1); 1.1323 + } 1.1324 + 1.1325 + options.password = strdup(optstate->value); 1.1326 + break; 1.1327 + 1.1328 + case 'f': 1.1329 + if (!optstate->value) { 1.1330 + fprintf(stderr, "%s: option -f must have a value.\n", progName); 1.1331 + Usage(progName); 1.1332 + exit(1); 1.1333 + } 1.1334 + 1.1335 + options.pwfile = strdup(optstate->value); 1.1336 + break; 1.1337 + 1.1338 + case 'r': 1.1339 + if (!optstate->value) { 1.1340 + fprintf(stderr, "%s: option -r must have a value.\n", progName); 1.1341 + Usage(progName); 1.1342 + exit(1); 1.1343 + } 1.1344 + envelopeOptions.recipients = ptrarray; 1.1345 + str = (char *)optstate->value; 1.1346 + do { 1.1347 + tok = strchr(str, ','); 1.1348 + if (tok) *tok = '\0'; 1.1349 + envelopeOptions.recipients[nrecipients++] = strdup(str); 1.1350 + if (tok) str = tok + 1; 1.1351 + } while (tok); 1.1352 + envelopeOptions.recipients[nrecipients] = NULL; 1.1353 + encryptOptions.recipients = envelopeOptions.recipients; 1.1354 + certsonlyOptions.recipients = envelopeOptions.recipients; 1.1355 + break; 1.1356 + 1.1357 + case 'u': { 1.1358 + int usageType; 1.1359 + 1.1360 + usageType = atoi (strdup(optstate->value)); 1.1361 + if (usageType < certUsageSSLClient || usageType > certUsageAnyCA) 1.1362 + return -1; 1.1363 + options.certUsage = (SECCertUsage)usageType; 1.1364 + break; 1.1365 + } 1.1366 + case 'v': 1.1367 + cms_verbose = 1; 1.1368 + break; 1.1369 + 1.1370 + } 1.1371 + } 1.1372 + if (status == PL_OPT_BAD) 1.1373 + Usage(progName); 1.1374 + PL_DestroyOptState(optstate); 1.1375 + 1.1376 + if (mode == UNKNOWN) 1.1377 + Usage(progName); 1.1378 + 1.1379 + if (mode != CERTSONLY && !batch) { 1.1380 + rv = SECU_FileToItem(&input, inFile); 1.1381 + if (rv != SECSuccess) { 1.1382 + SECU_PrintError(progName, "unable to read infile"); 1.1383 + exit(1); 1.1384 + } 1.1385 + if (inFile != PR_STDIN) { 1.1386 + PR_Close(inFile); 1.1387 + } 1.1388 + } 1.1389 + if (cms_verbose) { 1.1390 + fprintf(stderr, "received commands\n"); 1.1391 + } 1.1392 + 1.1393 + /* Call the NSS initialization routines */ 1.1394 + PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 1.1395 + rv = NSS_InitReadWrite(SECU_ConfigDirectory(NULL)); 1.1396 + if (SECSuccess != rv) { 1.1397 + SECU_PrintError(progName, "NSS_Init failed"); 1.1398 + exit(1); 1.1399 + } 1.1400 + if (cms_verbose) { 1.1401 + fprintf(stderr, "NSS has been initialized.\n"); 1.1402 + } 1.1403 + options.certHandle = CERT_GetDefaultCertDB(); 1.1404 + if (!options.certHandle) { 1.1405 + SECU_PrintError(progName, "No default cert DB"); 1.1406 + exit(1); 1.1407 + } 1.1408 + if (cms_verbose) { 1.1409 + fprintf(stderr, "Got default certdb\n"); 1.1410 + } 1.1411 + if (options.password) 1.1412 + { 1.1413 + pwdata.source = PW_PLAINTEXT; 1.1414 + pwdata.data = options.password; 1.1415 + } 1.1416 + if (options.pwfile) 1.1417 + { 1.1418 + pwdata.source = PW_FROMFILE; 1.1419 + pwdata.data = options.pwfile; 1.1420 + } 1.1421 + pwcb = SECU_GetModulePassword; 1.1422 + pwcb_arg = (void *)&pwdata; 1.1423 + 1.1424 + PK11_SetPasswordFunc(&SECU_GetModulePassword); 1.1425 + 1.1426 + 1.1427 +#if defined(_WIN32) 1.1428 + if (outFile == stdout) { 1.1429 + /* If we're going to write binary data to stdout, we must put stdout 1.1430 + ** into O_BINARY mode or else outgoing \n's will become \r\n's. 1.1431 + */ 1.1432 + int smrv = _setmode(_fileno(stdout), _O_BINARY); 1.1433 + if (smrv == -1) { 1.1434 + fprintf(stderr, 1.1435 + "%s: Cannot change stdout to binary mode. Use -o option instead.\n", 1.1436 + progName); 1.1437 + return smrv; 1.1438 + } 1.1439 + } 1.1440 +#endif 1.1441 + 1.1442 + exitstatus = 0; 1.1443 + switch (mode) { 1.1444 + case DECODE: /* -D */ 1.1445 + decodeOptions.options = &options; 1.1446 + if (encryptOptions.envFile) { 1.1447 + /* Decoding encrypted-data, so get the bulkkey from an 1.1448 + * enveloped-data message. 1.1449 + */ 1.1450 + SECU_FileToItem(&envmsg, encryptOptions.envFile); 1.1451 + decodeOptions.options = &options; 1.1452 + encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions); 1.1453 + if (!encryptOptions.envmsg) { 1.1454 + SECU_PrintError(progName, "problem decoding env msg"); 1.1455 + exitstatus = 1; 1.1456 + break; 1.1457 + } 1.1458 + rv = get_enc_params(&encryptOptions); 1.1459 + decodeOptions.dkcb = dkcb; 1.1460 + decodeOptions.bulkkey = encryptOptions.bulkkey; 1.1461 + } 1.1462 + if (!batch) { 1.1463 + cmsg = decode(outFile, &input, &decodeOptions); 1.1464 + if (!cmsg) { 1.1465 + SECU_PrintError(progName, "problem decoding"); 1.1466 + exitstatus = 1; 1.1467 + } 1.1468 + } else { 1.1469 + exitstatus = doBatchDecode(outFile, inFile, &decodeOptions); 1.1470 + if (inFile != PR_STDIN) { 1.1471 + PR_Close(inFile); 1.1472 + } 1.1473 + } 1.1474 + break; 1.1475 + case SIGN: /* -S */ 1.1476 + signOptions.options = &options; 1.1477 + cmsg = signed_data(&signOptions); 1.1478 + if (!cmsg) { 1.1479 + SECU_PrintError(progName, "problem signing"); 1.1480 + exitstatus = 1; 1.1481 + } 1.1482 + break; 1.1483 + case ENCRYPT: /* -C */ 1.1484 + if (!envFileName) { 1.1485 + fprintf(stderr, "%s: you must specify an envelope file with -e.\n", 1.1486 + progName); 1.1487 + exit(1); 1.1488 + } 1.1489 + encryptOptions.options = &options; 1.1490 + encryptOptions.input = &input; 1.1491 + encryptOptions.outfile = outFile; 1.1492 + /* decode an enveloped-data message to get the bulkkey (create 1.1493 + * a new one if neccessary) 1.1494 + */ 1.1495 + if (!encryptOptions.envFile) { 1.1496 + encryptOptions.envFile = PR_Open(envFileName, 1.1497 + PR_WRONLY|PR_CREATE_FILE, 00660); 1.1498 + if (!encryptOptions.envFile) { 1.1499 + fprintf(stderr, "%s: failed to create file %s.\n", progName, 1.1500 + envFileName); 1.1501 + exit(1); 1.1502 + } 1.1503 + } else { 1.1504 + SECU_FileToItem(&envmsg, encryptOptions.envFile); 1.1505 + decodeOptions.options = &options; 1.1506 + encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions); 1.1507 + if (encryptOptions.envmsg == NULL) { 1.1508 + SECU_PrintError(progName, "problem decrypting env msg"); 1.1509 + exitstatus = 1; 1.1510 + break; 1.1511 + } 1.1512 + } 1.1513 + rv = get_enc_params(&encryptOptions); 1.1514 + /* create the encrypted-data message */ 1.1515 + cmsg = encrypted_data(&encryptOptions); 1.1516 + if (!cmsg) { 1.1517 + SECU_PrintError(progName, "problem encrypting"); 1.1518 + exitstatus = 1; 1.1519 + } 1.1520 + if (encryptOptions.bulkkey) { 1.1521 + PK11_FreeSymKey(encryptOptions.bulkkey); 1.1522 + encryptOptions.bulkkey = NULL; 1.1523 + } 1.1524 + break; 1.1525 + case ENVELOPE: /* -E */ 1.1526 + envelopeOptions.options = &options; 1.1527 + cmsg = enveloped_data(&envelopeOptions); 1.1528 + if (!cmsg) { 1.1529 + SECU_PrintError(progName, "problem enveloping"); 1.1530 + exitstatus = 1; 1.1531 + } 1.1532 + break; 1.1533 + case CERTSONLY: /* -O */ 1.1534 + certsonlyOptions.options = &options; 1.1535 + cmsg = signed_data_certsonly(&certsonlyOptions); 1.1536 + if (!cmsg) { 1.1537 + SECU_PrintError(progName, "problem with certs-only"); 1.1538 + exitstatus = 1; 1.1539 + } 1.1540 + break; 1.1541 + default: 1.1542 + fprintf(stderr, "One of options -D, -S or -E must be set.\n"); 1.1543 + Usage(progName); 1.1544 + exitstatus = 1; 1.1545 + } 1.1546 + if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY) 1.1547 + && (!exitstatus) ) { 1.1548 + PLArenaPool *arena = PORT_NewArena(1024); 1.1549 + NSSCMSEncoderContext *ecx; 1.1550 + SECItem output = { 0, 0, 0 }; 1.1551 + 1.1552 + if (!arena) { 1.1553 + fprintf(stderr, "%s: out of memory.\n", progName); 1.1554 + exit(1); 1.1555 + } 1.1556 + 1.1557 + if (cms_verbose) { 1.1558 + fprintf(stderr, "cmsg [%p]\n", cmsg); 1.1559 + fprintf(stderr, "arena [%p]\n", arena); 1.1560 + if (pwcb_arg && (PW_PLAINTEXT == ((secuPWData*)pwcb_arg)->source)) 1.1561 + fprintf(stderr, "password [%s]\n", 1.1562 + ((secuPWData*)pwcb_arg)->data); 1.1563 + else 1.1564 + fprintf(stderr, "password [NULL]\n"); 1.1565 + } 1.1566 + ecx = NSS_CMSEncoder_Start(cmsg, 1.1567 + NULL, NULL, /* DER output callback */ 1.1568 + &output, arena, /* destination storage */ 1.1569 + pwcb, pwcb_arg, /* password callback */ 1.1570 + NULL, NULL, /* decrypt key callback */ 1.1571 + NULL, NULL ); /* detached digests */ 1.1572 + if (!ecx) { 1.1573 + fprintf(stderr, "%s: cannot create encoder context.\n", progName); 1.1574 + exit(1); 1.1575 + } 1.1576 + if (cms_verbose) { 1.1577 + fprintf(stderr, "input len [%d]\n", input.len); 1.1578 + { unsigned int j; 1.1579 + for(j=0;j<input.len;j++) 1.1580 + fprintf(stderr, "%2x%c", input.data[j], (j>0&&j%35==0)?'\n':' '); 1.1581 + } 1.1582 + } 1.1583 + if (input.len > 0) { /* skip if certs-only (or other zero content) */ 1.1584 + rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len); 1.1585 + if (rv) { 1.1586 + fprintf(stderr, 1.1587 + "%s: failed to add data to encoder.\n", progName); 1.1588 + exit(1); 1.1589 + } 1.1590 + } 1.1591 + rv = NSS_CMSEncoder_Finish(ecx); 1.1592 + if (rv) { 1.1593 + SECU_PrintError(progName, "failed to encode data"); 1.1594 + exit(1); 1.1595 + } 1.1596 + 1.1597 + if (cms_verbose) { 1.1598 + fprintf(stderr, "encoding passed\n"); 1.1599 + } 1.1600 + fwrite(output.data, output.len, 1, outFile); 1.1601 + if (cms_verbose) { 1.1602 + fprintf(stderr, "wrote to file\n"); 1.1603 + } 1.1604 + PORT_FreeArena(arena, PR_FALSE); 1.1605 + } 1.1606 + if (cmsg) 1.1607 + NSS_CMSMessage_Destroy(cmsg); 1.1608 + if (outFile != stdout) 1.1609 + fclose(outFile); 1.1610 + 1.1611 + SECITEM_FreeItem(&decodeOptions.content, PR_FALSE); 1.1612 + SECITEM_FreeItem(&envmsg, PR_FALSE); 1.1613 + SECITEM_FreeItem(&input, PR_FALSE); 1.1614 + if (NSS_Shutdown() != SECSuccess) { 1.1615 + SECU_PrintError(progName, "NSS_Shutdown failed"); 1.1616 + exitstatus = 1; 1.1617 + } 1.1618 + PR_Cleanup(); 1.1619 + return exitstatus; 1.1620 +}