Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/*
** secutil.c - various functions used by security stuff
**
*/
#include "prtypes.h"
#include "prtime.h"
#include "prlong.h"
#include "prerror.h"
#include "prprf.h"
#include "plgetopt.h"
#include "prenv.h"
#include "prnetdb.h"
#include "cryptohi.h"
#include "secutil.h"
#include "secpkcs7.h"
#include "secpkcs5.h"
#include <stdarg.h>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#ifdef XP_UNIX
#include <unistd.h>
#endif
/* for SEC_TraverseNames */
#include "cert.h"
#include "certt.h"
#include "certdb.h"
#include "secmod.h"
#include "pk11func.h"
#include "secoid.h"
static char consoleName[] = {
#ifdef XP_UNIX
"/dev/tty"
#else
#ifdef XP_OS2
"\\DEV\\CON"
#else
"CON:"
#endif
#endif
};
#include "nssutil.h"
#include "ssl.h"
#include "sslproto.h"
static PRBool utf8DisplayEnabled = PR_FALSE;
/* The minimum password/pin length (in Unicode characters) in FIPS mode,
* defined in lib/softoken/pkcs11i.h. */
#define FIPS_MIN_PIN 7
void
SECU_EnableUtf8Display(PRBool enable)
{
utf8DisplayEnabled = enable;
}
PRBool
SECU_GetUtf8DisplayEnabled(void)
{
return utf8DisplayEnabled;
}
static void
secu_ClearPassword(char *p)
{
if (p) {
PORT_Memset(p, 0, PORT_Strlen(p));
PORT_Free(p);
}
}
char *
SECU_GetPasswordString(void *arg, char *prompt)
{
#ifndef _WINDOWS
char *p = NULL;
FILE *input, *output;
/* open terminal */
input = fopen(consoleName, "r");
if (input == NULL) {
fprintf(stderr, "Error opening input terminal for read\n");
return NULL;
}
output = fopen(consoleName, "w");
if (output == NULL) {
fprintf(stderr, "Error opening output terminal for write\n");
fclose(input);
return NULL;
}
p = SEC_GetPassword(input, output, prompt, SEC_BlindCheckPassword);
fclose(input);
fclose(output);
return p;
#else
/* Win32 version of above. opening the console may fail
on windows95, and certainly isn't necessary.. */
char *p = NULL;
p = SEC_GetPassword(stdin, stdout, prompt, SEC_BlindCheckPassword);
return p;
#endif
}
/*
* p a s s w o r d _ h a r d c o d e
*
* A function to use the password passed in the -f(pwfile) argument
* of the command line.
* After use once, null it out otherwise PKCS11 calls us forever.?
*
*/
char *
SECU_FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg)
{
char *phrases, *phrase;
PRFileDesc *fd;
PRInt32 nb;
char *pwFile = arg;
int i;
const long maxPwdFileSize = 4096;
char *tokenName = NULL;
int tokenLen = 0;
if (!pwFile)
return 0;
if (retry) {
return 0; /* no good retrying - the files contents will be the same */
}
phrases = PORT_ZAlloc(maxPwdFileSize);
if (!phrases) {
return 0; /* out of memory */
}
fd = PR_Open(pwFile, PR_RDONLY, 0);
if (!fd) {
fprintf(stderr, "No password file \"%s\" exists.\n", pwFile);
PORT_Free(phrases);
return NULL;
}
nb = PR_Read(fd, phrases, maxPwdFileSize);
PR_Close(fd);
if (nb == 0) {
fprintf(stderr, "password file contains no data\n");
PORT_Free(phrases);
return NULL;
}
if (slot) {
tokenName = PK11_GetTokenName(slot);
if (tokenName) {
tokenLen = PORT_Strlen(tokenName);
}
}
i = 0;
do {
int startphrase = i;
int phraseLen;
/* handle the Windows EOL case */
while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb)
i++;
/* terminate passphrase */
phrases[i++] = '\0';
/* clean up any EOL before the start of the next passphrase */
while ((i < nb) && (phrases[i] == '\r' || phrases[i] == '\n')) {
phrases[i++] = '\0';
}
/* now analyze the current passphrase */
phrase = &phrases[startphrase];
if (!tokenName)
break;
if (PORT_Strncmp(phrase, tokenName, tokenLen))
continue;
phraseLen = PORT_Strlen(phrase);
if (phraseLen < (tokenLen + 1))
continue;
if (phrase[tokenLen] != ':')
continue;
phrase = &phrase[tokenLen + 1];
break;
} while (i < nb);
phrase = PORT_Strdup((char *)phrase);
PORT_Free(phrases);
return phrase;
}
char *
SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg)
{
char prompt[255];
secuPWData *pwdata = (secuPWData *)arg;
secuPWData pwnull = { PW_NONE, 0 };
secuPWData pwxtrn = { PW_EXTERNAL, "external" };
if (pwdata == NULL)
pwdata = &pwnull;
if (PK11_ProtectedAuthenticationPath(slot)) {
pwdata = &pwxtrn;
}
if (retry && pwdata->source != PW_NONE) {
PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n");
return NULL;
}
switch (pwdata->source) {
case PW_NONE:
snprintf(prompt, sizeof(prompt), "Enter Password or Pin for \"%s\":",
PK11_GetTokenName(slot));
return SECU_GetPasswordString(NULL, prompt);
case PW_FROMFILE:
return SECU_FilePasswd(slot, retry, pwdata->data);
case PW_EXTERNAL:
snprintf(prompt, sizeof(prompt),
"Press Enter, then enter PIN for \"%s\" on external device.\n",
PK11_GetTokenName(slot));
char *pw = SECU_GetPasswordString(NULL, prompt);
PORT_Free(pw);
/* Fall Through */
case PW_PLAINTEXT:
return PL_strdup(pwdata->data);
default:
break;
}
PR_fprintf(PR_STDERR, "Password check failed: No password found.\n");
return NULL;
}
char *
secu_InitSlotPassword(PK11SlotInfo *slot, PRBool retry, void *arg)
{
char *p0 = NULL;
char *p1 = NULL;
FILE *input, *output;
secuPWData *pwdata = arg;
if (pwdata->source == PW_FROMFILE) {
return SECU_FilePasswd(slot, retry, pwdata->data);
}
if (pwdata->source == PW_PLAINTEXT) {
return PL_strdup(pwdata->data);
}
/* PW_NONE - get it from tty */
/* open terminal */
#ifdef _WINDOWS
input = stdin;
#else
input = fopen(consoleName, "r");
#endif
if (input == NULL) {
PR_fprintf(PR_STDERR, "Error opening input terminal for read\n");
return NULL;
}
/* we have no password, so initialize database with one */
if (PK11_IsFIPS()) {
PR_fprintf(PR_STDERR,
"Enter a password which will be used to encrypt your keys.\n"
"The password should be at least %d characters long,\n"
"and should consist of at least three character classes.\n"
"The available character classes are: digits (0-9), ASCII\n"
"lowercase letters, ASCII uppercase letters, ASCII\n"
"non-alphanumeric characters, and non-ASCII characters.\n\n"
"If an ASCII uppercase letter appears at the beginning of\n"
"the password, it is not counted toward its character class.\n"
"Similarly, if a digit appears at the end of the password,\n"
"it is not counted toward its character class.\n\n",
FIPS_MIN_PIN);
} else {
PR_fprintf(PR_STDERR,
"Enter a password which will be used to encrypt your keys.\n"
"The password should be at least 8 characters long,\n"
"and should contain at least one non-alphabetic character.\n\n");
}
output = fopen(consoleName, "w");
if (output == NULL) {
PR_fprintf(PR_STDERR, "Error opening output terminal for write\n");
#ifndef _WINDOWS
fclose(input);
#endif
return NULL;
}
for (;;) {
if (p0)
PORT_Free(p0);
p0 = SEC_GetPassword(input, output, "Enter new password: ",
SEC_BlindCheckPassword);
if (p1)
PORT_Free(p1);
p1 = SEC_GetPassword(input, output, "Re-enter password: ",
SEC_BlindCheckPassword);
if (p0 && p1 && !PORT_Strcmp(p0, p1)) {
break;
}
PR_fprintf(PR_STDERR, "Passwords do not match. Try again.\n");
}
/* clear out the duplicate password string */
secu_ClearPassword(p1);
fclose(input);
fclose(output);
return p0;
}
SECStatus
SECU_ChangePW(PK11SlotInfo *slot, char *passwd, char *pwFile)
{
return SECU_ChangePW2(slot, passwd, 0, pwFile, 0);
}
SECStatus
SECU_ChangePW2(PK11SlotInfo *slot, char *oldPass, char *newPass,
char *oldPwFile, char *newPwFile)
{
SECStatus rv;
secuPWData pwdata, newpwdata;
char *oldpw = NULL, *newpw = NULL;
if (oldPass) {
pwdata.source = PW_PLAINTEXT;
pwdata.data = oldPass;
} else if (oldPwFile) {
pwdata.source = PW_FROMFILE;
pwdata.data = oldPwFile;
} else {
pwdata.source = PW_NONE;
pwdata.data = NULL;
}
if (newPass) {
newpwdata.source = PW_PLAINTEXT;
newpwdata.data = newPass;
} else if (newPwFile) {
newpwdata.source = PW_FROMFILE;
newpwdata.data = newPwFile;
} else {
newpwdata.source = PW_NONE;
newpwdata.data = NULL;
}
if (PK11_NeedUserInit(slot)) {
newpw = secu_InitSlotPassword(slot, PR_FALSE, &pwdata);
rv = PK11_InitPin(slot, (char *)NULL, newpw);
goto done;
}
for (;;) {
oldpw = SECU_GetModulePassword(slot, PR_FALSE, &pwdata);
if (PK11_CheckUserPassword(slot, oldpw) != SECSuccess) {
if (pwdata.source == PW_NONE) {
PR_fprintf(PR_STDERR, "Invalid password. Try again.\n");
} else {
PR_fprintf(PR_STDERR, "Invalid password.\n");
PORT_Memset(oldpw, 0, PL_strlen(oldpw));
PORT_Free(oldpw);
rv = SECFailure;
goto done;
}
} else
break;
PORT_Free(oldpw);
}
newpw = secu_InitSlotPassword(slot, PR_FALSE, &newpwdata);
rv = PK11_ChangePW(slot, oldpw, newpw);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Failed to change password.\n");
} else {
PR_fprintf(PR_STDOUT, "Password changed successfully.\n");
}
PORT_Memset(oldpw, 0, PL_strlen(oldpw));
PORT_Free(oldpw);
done:
if (newpw) {
PORT_Memset(newpw, 0, PL_strlen(newpw));
PORT_Free(newpw);
}
return rv;
}
struct matchobj {
SECItem index;
char *nname;
PRBool found;
};
char *
SECU_DefaultSSLDir(void)
{
char *dir;
static char sslDir[1000];
dir = PR_GetEnvSecure("SSL_DIR");
if (!dir)
return NULL;
if (strlen(dir) >= PR_ARRAY_SIZE(sslDir)) {
return NULL;
}
snprintf(sslDir, sizeof(sslDir), "%s", dir);
if (sslDir[strlen(sslDir) - 1] == '/')
sslDir[strlen(sslDir) - 1] = 0;
return sslDir;
}
char *
SECU_AppendFilenameToDir(char *dir, char *filename)
{
static char path[1000];
if (dir[strlen(dir) - 1] == '/')
snprintf(path, sizeof(path), "%s%s", dir, filename);
else
snprintf(path, sizeof(path), "%s/%s", dir, filename);
return path;
}
char *
SECU_ConfigDirectory(const char *base)
{
static PRBool initted = PR_FALSE;
const char *dir = ".netscape";
char *home;
static char buf[1000];
if (initted)
return buf;
if (base == NULL || *base == 0) {
home = PR_GetEnvSecure("HOME");
if (!home)
home = "";
if (*home && home[strlen(home) - 1] == '/')
snprintf(buf, sizeof(buf), "%.900s%s", home, dir);
else
snprintf(buf, sizeof(buf), "%.900s/%s", home, dir);
} else {
snprintf(buf, sizeof(buf), "%.900s", base);
if (buf[strlen(buf) - 1] == '/')
buf[strlen(buf) - 1] = 0;
}
initted = PR_TRUE;
return buf;
}
SECStatus
SECU_ReadDERFromFile(SECItem *der, PRFileDesc *inFile, PRBool ascii,
PRBool warnOnPrivateKeyInAsciiFile)
{
SECStatus rv;
if (ascii) {
/* First convert ascii to binary */
SECItem filedata;
/* Read in ascii data */
rv = SECU_FileToItem(&filedata, inFile);
if (rv != SECSuccess)
return rv;
if (!filedata.data) {
fprintf(stderr, "unable to read data from input file\n");
return SECFailure;
}
/* need one additional byte for zero terminator */
rv = SECITEM_ReallocItemV2(NULL, &filedata, filedata.len + 1);
if (rv != SECSuccess) {
PORT_Free(filedata.data);
return rv;
}
char *asc = (char *)filedata.data;
asc[filedata.len - 1] = '\0';
if (warnOnPrivateKeyInAsciiFile && strstr(asc, "PRIVATE KEY")) {
fprintf(stderr, "Warning: ignoring private key. Consider to use "
"pk12util.\n");
}
char *body;
/* check for headers and trailers and remove them */
if ((body = strstr(asc, "-----BEGIN")) != NULL) {
char *trailer = NULL;
asc = body;
body = PORT_Strchr(body, '\n');
if (!body)
body = PORT_Strchr(asc, '\r'); /* maybe this is a MAC file */
if (body)
trailer = strstr(++body, "-----END");
if (trailer != NULL) {
*trailer = '\0';
} else {
fprintf(stderr, "input has header but no trailer\n");
PORT_Free(filedata.data);
return SECFailure;
}
} else {
body = asc;
}
/* Convert to binary */
rv = ATOB_ConvertAsciiToItem(der, body);
if (rv != SECSuccess) {
fprintf(stderr, "error converting ascii to binary (%s)\n",
SECU_Strerror(PORT_GetError()));
PORT_Free(filedata.data);
return SECFailure;
}
PORT_Free(filedata.data);
} else {
/* Read in binary der */
rv = SECU_FileToItem(der, inFile);
if (rv != SECSuccess) {
fprintf(stderr, "error converting der (%s)\n",
SECU_Strerror(PORT_GetError()));
return SECFailure;
}
}
return SECSuccess;
}
#define INDENT_MULT 4
/*
* remove the tag and length and just leave the bare BER data
*/
SECStatus
SECU_StripTagAndLength(SECItem *i)
{
unsigned int start;
PRBool isIndefinite;
if (!i || !i->data || i->len < 2) { /* must be at least tag and length */
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
isIndefinite = (i->data[1] == 0x80);
start = ((i->data[1] & 0x80) ? (i->data[1] & 0x7f) + 2 : 2);
if (i->len < start) {
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
i->data += start;
i->len -= start;
/* we are using indefinite encoding, drop the trailing zero */
if (isIndefinite) {
if (i->len <= 1) {
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
/* verify tags are zero */
if ((i->data[i->len - 1] != 0) || (i->data[i->len - 2] != 0)) {
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
i->len -= 2;
}
return SECSuccess;
}
/*
* Create a new SECItem which points to the current BER tag and length with
* all it's data. For indefinite encoding, this will also include the trailing
* indefinite markers
* The 'in' item is advanced to point to the next BER tag.
* You don't want to use this in an actual BER/DER parser as NSS already
* has 3 to choose from)
*/
SECStatus
SECU_ExtractBERAndStep(SECItem *in, SECItem *out)
{
if (!in || !in->data || in->len < 2) { /* must be at least tag and length */
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
*out = *in;
/* first handle indefinite encoding */
if (out->data[1] == 0x80) {
SECItem this = *out;
SECItem next;
this.data += 2;
this.len -= 2;
out->len = 2;
/* walk through all the entries until we find the '0' */
while ((this.len >= 2) && (this.data[0] != 0)) {
SECStatus rv = SECU_ExtractBERAndStep(&this, &next);
if (rv != SECSuccess) {
return rv;
}
out->len += next.len;
}
if ((this.len < 2) || ((this.data[0] != 0) && (this.data[1] != 0))) {
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
out->len += 2; /* include the trailing zeros */
in->data += out->len;
in->len -= out->len;
return SECSuccess;
}
/* now handle normal DER encoding */
if (out->data[1] & 0x80) {
unsigned int i;
unsigned int lenlen = out->data[1] & 0x7f;
unsigned int len = 0;
if (lenlen > sizeof out->len) {
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
for (i = 0; i < lenlen; i++) {
len = (len << 8) | out->data[2 + i];
}
out->len = len + lenlen + 2;
} else {
out->len = out->data[1] + 2;
}
if (out->len > in->len) {
/* we've ran into a truncated file */
PORT_SetError(SEC_ERROR_BAD_DER);
return SECFailure;
}
in->data += out->len;
in->len -= out->len;
return SECSuccess;
}
static void
secu_PrintRawStringQuotesOptional(FILE *out, SECItem *si, const char *m,
int level, PRBool quotes)
{
int column;
unsigned int i;
if (m) {
SECU_Indent(out, level);
fprintf(out, "%s: ", m);
column = (level * INDENT_MULT) + strlen(m) + 2;
level++;
} else {
SECU_Indent(out, level);
column = level * INDENT_MULT;
}
if (quotes) {
fprintf(out, "\"");
column++;
}
for (i = 0; i < si->len; i++) {
unsigned char val = si->data[i];
unsigned char c;
if (SECU_GetWrapEnabled() && column > 76) {
SECU_Newline(out);
SECU_Indent(out, level);
column = level * INDENT_MULT;
}
if (utf8DisplayEnabled) {
if (val < 32)
c = '.';
else
c = val;
} else {
c = printable[val];
}
fprintf(out, "%c", c);
column++;
}
if (quotes) {
fprintf(out, "\"");
column++;
}
if (SECU_GetWrapEnabled() &&
(column != level * INDENT_MULT || column > 76)) {
SECU_Newline(out);
}
}
static void
secu_PrintRawString(FILE *out, SECItem *si, const char *m, int level)
{
secu_PrintRawStringQuotesOptional(out, si, m, level, PR_TRUE);
}
void
SECU_PrintString(FILE *out, const SECItem *si, const char *m, int level)
{
SECItem my = *si;
if (SECSuccess != SECU_StripTagAndLength(&my) || !my.len)
return;
secu_PrintRawString(out, &my, m, level);
}
/* print an unencoded boolean */
static void
secu_PrintBoolean(FILE *out, SECItem *i, const char *m, int level)
{
int val = 0;
if (i->data && i->len) {
val = i->data[0];
}
if (!m) {
m = "Boolean";
}
SECU_Indent(out, level);
fprintf(out, "%s: %s\n", m, (val ? "True" : "False"));
}
/*
* Format and print "time". If the tag message "m" is not NULL,
* do indent formatting based on "level" and add a newline afterward;
* otherwise just print the formatted time string only.
*/
static void
secu_PrintTime(FILE *out, const PRTime time, const char *m, int level)
{
PRExplodedTime printableTime;
char *timeString;
/* Convert to local time */
PR_ExplodeTime(time, PR_GMTParameters, &printableTime);
timeString = PORT_Alloc(256);
if (timeString == NULL)
return;
if (m != NULL) {
SECU_Indent(out, level);
fprintf(out, "%s: ", m);
}
if (PR_FormatTime(timeString, 256, "%a %b %d %H:%M:%S %Y", &printableTime)) {
fputs(timeString, out);
}
if (m != NULL)
fprintf(out, "\n");
PORT_Free(timeString);
}
/*
* Format and print the UTC Time "t". If the tag message "m" is not NULL,
* do indent formatting based on "level" and add a newline afterward;
* otherwise just print the formatted time string only.
*/
void
SECU_PrintUTCTime(FILE *out, const SECItem *t, const char *m, int level)
{
PRTime time;
SECStatus rv;
rv = DER_UTCTimeToTime(&time, t);
if (rv != SECSuccess)
return;
secu_PrintTime(out, time, m, level);
}
/*
* Format and print the Generalized Time "t". If the tag message "m"
* is not NULL, * do indent formatting based on "level" and add a newline
* afterward; otherwise just print the formatted time string only.
*/
void
SECU_PrintGeneralizedTime(FILE *out, const SECItem *t, const char *m, int level)
{
PRTime time;
SECStatus rv;
rv = DER_GeneralizedTimeToTime(&time, t);
if (rv != SECSuccess)
return;
secu_PrintTime(out, time, m, level);
}
/*
* Format and print the UTC or Generalized Time "t". If the tag message
* "m" is not NULL, do indent formatting based on "level" and add a newline
* afterward; otherwise just print the formatted time string only.
*/
void
SECU_PrintTimeChoice(FILE *out, const SECItem *t, const char *m, int level)
{
switch (t->type) {
case siUTCTime:
SECU_PrintUTCTime(out, t, m, level);
break;
case siGeneralizedTime:
SECU_PrintGeneralizedTime(out, t, m, level);
break;
default:
PORT_Assert(0);
break;
}
}
/* This prints a SET or SEQUENCE */
static void
SECU_PrintSet(FILE *out, const SECItem *t, const char *m, int level)
{
int type = t->data[0] & SEC_ASN1_TAGNUM_MASK;
int constructed = t->data[0] & SEC_ASN1_CONSTRUCTED;
const char *label;
SECItem my = *t;
if (!constructed) {
SECU_PrintAsHex(out, t, m, level);
return;
}
if (SECSuccess != SECU_StripTagAndLength(&my))
return;
SECU_Indent(out, level);
if (m) {
fprintf(out, "%s: ", m);
}
if (type == SEC_ASN1_SET)
label = "Set ";
else if (type == SEC_ASN1_SEQUENCE)
label = "Sequence ";
else
label = "";
fprintf(out, "%s{\n", label); /* } */
while (my.len >= 2) {
SECItem tmp;
if (SECSuccess != SECU_ExtractBERAndStep(&my, &tmp)) {
break;
}
SECU_PrintAny(out, &tmp, NULL, level + 1);
}
SECU_Indent(out, level);
fprintf(out, /* { */ "}\n");
}
static void
secu_PrintContextSpecific(FILE *out, const SECItem *i, const char *m, int level)
{
int type = i->data[0] & SEC_ASN1_TAGNUM_MASK;
int constructed = i->data[0] & SEC_ASN1_CONSTRUCTED;
SECItem tmp;
if (constructed) {
char *m2;
if (!m)
m2 = PR_smprintf("[%d]", type);
else
m2 = PR_smprintf("%s: [%d]", m, type);
if (m2) {
SECU_PrintSet(out, i, m2, level);
PR_smprintf_free(m2);
}
return;
}
SECU_Indent(out, level);
if (m) {
fprintf(out, "%s: ", m);
}
fprintf(out, "[%d]\n", type);
tmp = *i;
if (SECSuccess == SECU_StripTagAndLength(&tmp))
SECU_PrintAsHex(out, &tmp, m, level + 1);
}
static void
secu_PrintOctetString(FILE *out, const SECItem *i, const char *m, int level)
{
SECItem tmp = *i;
if (SECSuccess == SECU_StripTagAndLength(&tmp))
SECU_PrintAsHex(out, &tmp, m, level);
}
static void
secu_PrintBitString(FILE *out, const SECItem *i, const char *m, int level)
{
int unused_bits;
SECItem tmp = *i;
if (SECSuccess != SECU_StripTagAndLength(&tmp) || tmp.len < 2)
return;
unused_bits = *tmp.data++;
tmp.len--;
SECU_PrintAsHex(out, &tmp, m, level);
if (unused_bits) {
SECU_Indent(out, level + 1);
fprintf(out, "(%d least significant bits unused)\n", unused_bits);
}
}
/* in a decoded bit string, the len member is a bit length. */
static void
secu_PrintDecodedBitString(FILE *out, const SECItem *i, const char *m, int level)
{
int unused_bits;
SECItem tmp = *i;
unused_bits = (tmp.len & 0x7) ? 8 - (tmp.len & 7) : 0;
DER_ConvertBitString(&tmp); /* convert length to byte length */
SECU_PrintAsHex(out, &tmp, m, level);
if (unused_bits) {
SECU_Indent(out, level + 1);
fprintf(out, "(%d least significant bits unused)\n", unused_bits);
}
}
/* Print a DER encoded Boolean */
void
SECU_PrintEncodedBoolean(FILE *out, const SECItem *i, const char *m, int level)
{
SECItem my = *i;
if (SECSuccess == SECU_StripTagAndLength(&my))
secu_PrintBoolean(out, &my, m, level);
}
/* Print a DER encoded integer */
void
SECU_PrintEncodedInteger(FILE *out, const SECItem *i, const char *m, int level)
{
SECItem my = *i;
if (SECSuccess == SECU_StripTagAndLength(&my))
SECU_PrintInteger(out, &my, m, level);
}
/* Print a DER encoded OID */
SECOidTag
SECU_PrintEncodedObjectID(FILE *out, const SECItem *i, const char *m, int level)
{
SECItem my = *i;
SECOidTag tag = SEC_OID_UNKNOWN;
if (SECSuccess == SECU_StripTagAndLength(&my))
tag = SECU_PrintObjectID(out, &my, m, level);
return tag;
}
static void
secu_PrintBMPString(FILE *out, const SECItem *i, const char *m, int level)
{
unsigned char *s;
unsigned char *d;
int len;
SECItem tmp = { 0, 0, 0 };
SECItem my = *i;
if (SECSuccess != SECU_StripTagAndLength(&my))
goto loser;
if (my.len % 2)
goto loser;
len = (int)(my.len / 2);
tmp.data = (unsigned char *)PORT_Alloc(len);
if (!tmp.data)
goto loser;
tmp.len = len;
for (s = my.data, d = tmp.data; len > 0; len--) {
PRUint32 bmpChar = (s[0] << 8) | s[1];
s += 2;
if (!isprint(bmpChar))
goto loser;
*d++ = (unsigned char)bmpChar;
}
secu_PrintRawString(out, &tmp, m, level);
PORT_Free(tmp.data);
return;
loser:
SECU_PrintAsHex(out, i, m, level);
if (tmp.data)
PORT_Free(tmp.data);
}
static void
secu_PrintUniversalString(FILE *out, const SECItem *i, const char *m, int level)
{
unsigned char *s;
unsigned char *d;
int len;
SECItem tmp = { 0, 0, 0 };
SECItem my = *i;
if (SECSuccess != SECU_StripTagAndLength(&my))
goto loser;
if (my.len % 4)
goto loser;
len = (int)(my.len / 4);
tmp.data = (unsigned char *)PORT_Alloc(len);
if (!tmp.data)
goto loser;
tmp.len = len;
for (s = my.data, d = tmp.data; len > 0; len--) {
PRUint32 bmpChar = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
s += 4;
if (!isprint(bmpChar & 0xFF))
goto loser;
*d++ = (unsigned char)bmpChar;
}
secu_PrintRawString(out, &tmp, m, level);
PORT_Free(tmp.data);
return;
loser:
SECU_PrintAsHex(out, i, m, level);
if (tmp.data)
PORT_Free(tmp.data);
}
static void
secu_PrintUniversal(FILE *out, const SECItem *i, const char *m, int level)
{
switch (i->data[0] & SEC_ASN1_TAGNUM_MASK) {
case SEC_ASN1_ENUMERATED:
case SEC_ASN1_INTEGER:
SECU_PrintEncodedInteger(out, i, m, level);
break;
case SEC_ASN1_OBJECT_ID:
SECU_PrintEncodedObjectID(out, i, m, level);
break;
case SEC_ASN1_BOOLEAN:
SECU_PrintEncodedBoolean(out, i, m, level);
break;
case SEC_ASN1_UTF8_STRING:
case SEC_ASN1_PRINTABLE_STRING:
case SEC_ASN1_VISIBLE_STRING:
case SEC_ASN1_IA5_STRING:
case SEC_ASN1_T61_STRING:
SECU_PrintString(out, i, m, level);
break;
case SEC_ASN1_GENERALIZED_TIME:
SECU_PrintGeneralizedTime(out, i, m, level);
break;
case SEC_ASN1_UTC_TIME:
SECU_PrintUTCTime(out, i, m, level);
break;
case SEC_ASN1_NULL:
SECU_Indent(out, level);
if (m && m[0])
fprintf(out, "%s: NULL\n", m);
else
fprintf(out, "NULL\n");
break;
case SEC_ASN1_SET:
case SEC_ASN1_SEQUENCE:
SECU_PrintSet(out, i, m, level);
break;
case SEC_ASN1_OCTET_STRING:
secu_PrintOctetString(out, i, m, level);
break;
case SEC_ASN1_BIT_STRING:
secu_PrintBitString(out, i, m, level);
break;
case SEC_ASN1_BMP_STRING:
secu_PrintBMPString(out, i, m, level);
break;
case SEC_ASN1_UNIVERSAL_STRING:
secu_PrintUniversalString(out, i, m, level);
break;
default:
SECU_PrintAsHex(out, i, m, level);
break;
}
}
void
SECU_PrintAny(FILE *out, const SECItem *i, const char *m, int level)
{
if (i && i->len && i->data) {
switch (i->data[0] & SEC_ASN1_CLASS_MASK) {
case SEC_ASN1_CONTEXT_SPECIFIC:
secu_PrintContextSpecific(out, i, m, level);
break;
case SEC_ASN1_UNIVERSAL:
secu_PrintUniversal(out, i, m, level);
break;
default:
SECU_PrintAsHex(out, i, m, level);
break;
}
}
}
static int
secu_PrintValidity(FILE *out, CERTValidity *v, char *m, int level)
{
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
SECU_PrintTimeChoice(out, &v->notBefore, "Not Before", level + 1);
SECU_PrintTimeChoice(out, &v->notAfter, "Not After ", level + 1);
return 0;
}
/* This function does NOT expect a DER type and length. */
SECOidTag
SECU_PrintObjectID(FILE *out, const SECItem *oid, const char *m, int level)
{
SECOidData *oiddata;
char *oidString = NULL;
oiddata = SECOID_FindOID(oid);
if (oiddata != NULL) {
const char *name = oiddata->desc;
SECU_Indent(out, level);
if (m != NULL)
fprintf(out, "%s: ", m);
fprintf(out, "%s\n", name);
return oiddata->offset;
}
oidString = CERT_GetOidString(oid);
if (oidString) {
SECU_Indent(out, level);
if (m != NULL)
fprintf(out, "%s: ", m);
fprintf(out, "%s\n", oidString);
PR_smprintf_free(oidString);
return SEC_OID_UNKNOWN;
}
SECU_PrintAsHex(out, oid, m, level);
return SEC_OID_UNKNOWN;
}
typedef struct secuPBEParamsStr {
SECItem salt;
SECItem iterationCount;
SECItem keyLength;
SECAlgorithmID cipherAlg;
SECAlgorithmID kdfAlg;
} secuPBEParams;
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
/* SECOID_PKCS5_PBKDF2 */
const SEC_ASN1Template secuKDF2Params[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) },
{ SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) },
{ SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) },
{ SEC_ASN1_INTEGER, offsetof(secuPBEParams, keyLength) },
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg),
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
{ 0 }
};
/* PKCS5v1 & PKCS12 */
const SEC_ASN1Template secuPBEParamsTemp[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) },
{ SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) },
{ SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) },
{ 0 }
};
/* SEC_OID_PKCS5_PBES2, SEC_OID_PKCS5_PBMAC1 */
const SEC_ASN1Template secuPBEV2Params[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) },
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg),
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, cipherAlg),
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
{ 0 }
};
void
secu_PrintRSAPSSParams(FILE *out, SECItem *value, char *m, int level)
{
PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
SECStatus rv;
SECKEYRSAPSSParams param;
SECAlgorithmID maskHashAlg;
if (m) {
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
}
if (!pool) {
SECU_Indent(out, level);
fprintf(out, "Out of memory\n");
return;
}
PORT_Memset(¶m, 0, sizeof param);
rv = SEC_QuickDERDecodeItem(pool, ¶m,
SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate),
value);
if (rv == SECSuccess) {
if (!param.hashAlg) {
SECU_Indent(out, level + 1);
fprintf(out, "Hash algorithm: default, SHA-1\n");
} else {
SECU_PrintObjectID(out, ¶m.hashAlg->algorithm,
"Hash algorithm", level + 1);
}
if (!param.maskAlg) {
SECU_Indent(out, level + 1);
fprintf(out, "Mask algorithm: default, MGF1\n");
SECU_Indent(out, level + 1);
fprintf(out, "Mask hash algorithm: default, SHA-1\n");
} else {
SECU_PrintObjectID(out, ¶m.maskAlg->algorithm,
"Mask algorithm", level + 1);
rv = SEC_QuickDERDecodeItem(pool, &maskHashAlg,
SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
¶m.maskAlg->parameters);
if (rv == SECSuccess) {
SECU_PrintObjectID(out, &maskHashAlg.algorithm,
"Mask hash algorithm", level + 1);
} else {
SECU_Indent(out, level + 1);
fprintf(out, "Invalid mask generation algorithm parameters\n");
}
}
if (!param.saltLength.data) {
SECU_Indent(out, level + 1);
fprintf(out, "Salt length: default, %i (0x%2X)\n", 20, 20);
} else {
SECU_PrintInteger(out, ¶m.saltLength, "Salt length", level + 1);
}
} else {
SECU_Indent(out, level + 1);
fprintf(out, "Invalid RSA-PSS parameters\n");
}
PORT_FreeArena(pool, PR_FALSE);
}
void
secu_PrintKDF2Params(FILE *out, SECItem *value, char *m, int level)
{
PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
SECStatus rv;
secuPBEParams param;
if (m) {
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
}
if (!pool) {
SECU_Indent(out, level);
fprintf(out, "Out of memory\n");
return;
}
PORT_Memset(¶m, 0, sizeof param);
rv = SEC_QuickDERDecodeItem(pool, ¶m, secuKDF2Params, value);
if (rv == SECSuccess) {
SECU_PrintAsHex(out, ¶m.salt, "Salt", level + 1);
SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count",
level + 1);
SECU_PrintInteger(out, ¶m.keyLength, "Key Length", level + 1);
SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF algorithm", level + 1);
}
PORT_FreeArena(pool, PR_FALSE);
}
void
secu_PrintPKCS5V2Params(FILE *out, SECItem *value, char *m, int level)
{
PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
SECStatus rv;
secuPBEParams param;
if (m) {
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
}
if (!pool) {
SECU_Indent(out, level);
fprintf(out, "Out of memory\n");
return;
}
PORT_Memset(¶m, 0, sizeof param);
rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEV2Params, value);
if (rv == SECSuccess) {
SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF", level + 1);
SECU_PrintAlgorithmID(out, ¶m.cipherAlg, "Cipher", level + 1);
}
PORT_FreeArena(pool, PR_FALSE);
}
void
secu_PrintPBEParams(FILE *out, SECItem *value, char *m, int level)
{
PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
SECStatus rv;
secuPBEParams param;
if (m) {
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
}
if (!pool) {
SECU_Indent(out, level);
fprintf(out, "Out of memory\n");
return;
}
PORT_Memset(¶m, 0, sizeof(secuPBEParams));
rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEParamsTemp, value);
if (rv == SECSuccess) {
SECU_PrintAsHex(out, ¶m.salt, "Salt", level + 1);
SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count",
level + 1);
}
PORT_FreeArena(pool, PR_FALSE);
}
/* This function does NOT expect a DER type and length. */
void
SECU_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m, int level)
{
SECOidTag algtag;
SECU_PrintObjectID(out, &a->algorithm, m, level);
algtag = SECOID_GetAlgorithmTag(a);
if (SEC_PKCS5IsAlgorithmPBEAlgTag(algtag)) {
switch (algtag) {
case SEC_OID_PKCS5_PBKDF2:
secu_PrintKDF2Params(out, &a->parameters, "Parameters", level + 1);
break;
case SEC_OID_PKCS5_PBES2:
secu_PrintPKCS5V2Params(out, &a->parameters, "Encryption", level + 1);
break;
case SEC_OID_PKCS5_PBMAC1:
secu_PrintPKCS5V2Params(out, &a->parameters, "MAC", level + 1);
break;
default:
secu_PrintPBEParams(out, &a->parameters, "Parameters", level + 1);
break;
}
return;
}
if (a->parameters.len == 0 ||
(a->parameters.len == 2 &&
PORT_Memcmp(a->parameters.data, "\005\000", 2) == 0)) {
/* No arguments or NULL argument */
} else if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1);
} else {
/* Print args to algorithm */
SECU_PrintAsHex(out, &a->parameters, "Args", level + 1);
}
}
static void
secu_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m, int level)
{
SECItem *value;
int i;
char om[100];
if (m) {
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
}
/*
* Should make this smarter; look at the type field and then decode
* and print the value(s) appropriately!
*/
SECU_PrintObjectID(out, &(attr->type), "Type", level + 1);
if (attr->values != NULL) {
i = 0;
while ((value = attr->values[i++]) != NULL) {
snprintf(om, sizeof(om), "Value (%d)%s", i, attr->encoded ? " (encoded)" : "");
if (attr->encoded || attr->typeTag == NULL) {
SECU_PrintAny(out, value, om, level + 1);
} else {
switch (attr->typeTag->offset) {
default:
SECU_PrintAsHex(out, value, om, level + 1);
break;
case SEC_OID_PKCS9_CONTENT_TYPE:
SECU_PrintObjectID(out, value, om, level + 1);
break;
case SEC_OID_PKCS9_SIGNING_TIME:
SECU_PrintTimeChoice(out, value, om, level + 1);
break;
}
}
}
}
}
static void
secu_PrintECPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level)
{
SECItem curveOID = { siBuffer, NULL, 0 };
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
SECU_PrintInteger(out, &pk->u.ec.publicValue, "PublicValue", level + 1);
/* For named curves, the DEREncodedParams field contains an
* ASN Object ID (0x06 is SEC_ASN1_OBJECT_ID).
*/
if ((pk->u.ec.DEREncodedParams.len > 2) &&
(pk->u.ec.DEREncodedParams.data[0] == 0x06)) {
curveOID.len = pk->u.ec.DEREncodedParams.data[1];
curveOID.data = pk->u.ec.DEREncodedParams.data + 2;
curveOID.len = PR_MIN(curveOID.len, pk->u.ec.DEREncodedParams.len - 2);
SECU_PrintObjectID(out, &curveOID, "Curve", level + 1);
}
}
void
SECU_PrintRSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level)
{
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
SECU_PrintInteger(out, &pk->u.rsa.modulus, "Modulus", level + 1);
SECU_PrintInteger(out, &pk->u.rsa.publicExponent, "Exponent", level + 1);
if (pk->u.rsa.publicExponent.len == 1 &&
pk->u.rsa.publicExponent.data[0] == 1) {
SECU_Indent(out, level + 1);
fprintf(out, "Error: INVALID RSA KEY!\n");
}
}
void
SECU_PrintDSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level)
{
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
SECU_PrintInteger(out, &pk->u.dsa.params.prime, "Prime", level + 1);
SECU_PrintInteger(out, &pk->u.dsa.params.subPrime, "Subprime", level + 1);
SECU_PrintInteger(out, &pk->u.dsa.params.base, "Base", level + 1);
SECU_PrintInteger(out, &pk->u.dsa.publicValue, "PublicValue", level + 1);
}
static void
secu_PrintSubjectPublicKeyInfo(FILE *out, PLArenaPool *arena,
CERTSubjectPublicKeyInfo *i, char *msg, int level)
{
SECKEYPublicKey *pk;
SECU_Indent(out, level);
fprintf(out, "%s:\n", msg);
SECU_PrintAlgorithmID(out, &i->algorithm, "Public Key Algorithm", level + 1);
pk = SECKEY_ExtractPublicKey(i);
if (pk) {
switch (pk->keyType) {
case rsaKey:
SECU_PrintRSAPublicKey(out, pk, "RSA Public Key", level + 1);
break;
case dsaKey:
SECU_PrintDSAPublicKey(out, pk, "DSA Public Key", level + 1);
break;
case ecKey:
secu_PrintECPublicKey(out, pk, "EC Public Key", level + 1);
break;
case dhKey:
case fortezzaKey:
case keaKey:
SECU_Indent(out, level);
fprintf(out, "unable to format this SPKI algorithm type\n");
goto loser;
default:
SECU_Indent(out, level);
fprintf(out, "unknown SPKI algorithm type\n");
goto loser;
}
PORT_FreeArena(pk->arena, PR_FALSE);
} else {
SECU_PrintErrMsg(out, level, "Error", "Parsing public key");
loser:
if (i->subjectPublicKey.data) {
SECU_PrintAny(out, &i->subjectPublicKey, "Raw", level);
}
}
}
static void
printStringWithoutCRLF(FILE *out, const char *str)
{
const char *c = str;
while (*c) {
if (*c != '\r' && *c != '\n') {
fputc(*c, out);
}
++c;
}
}
int
SECU_PrintDumpDerIssuerAndSerial(FILE *out, SECItem *der, char *m,
int level)
{
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
CERTCertificate *c;
int rv = SEC_ERROR_NO_MEMORY;
char *derIssuerB64;
char *derSerialB64;
if (!arena)
return rv;
/* Decode certificate */
c = PORT_ArenaZNew(arena, CERTCertificate);
if (!c)
goto loser;
c->arena = arena;
rv = SEC_ASN1DecodeItem(arena, c,
SEC_ASN1_GET(CERT_CertificateTemplate), der);
if (rv) {
SECU_PrintErrMsg(out, 0, "Error", "Parsing extension");
goto loser;
}
SECU_PrintName(out, &c->subject, "Subject", 0);
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
SECU_Newline(out);
SECU_PrintName(out, &c->issuer, "Issuer", 0);
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
SECU_Newline(out);
SECU_PrintInteger(out, &c->serialNumber, "Serial Number", 0);
derIssuerB64 = BTOA_ConvertItemToAscii(&c->derIssuer);
derSerialB64 = BTOA_ConvertItemToAscii(&c->serialNumber);
fprintf(out, "Issuer DER Base64:\n");
if (SECU_GetWrapEnabled()) {
fprintf(out, "%s\n", derIssuerB64);
} else {
printStringWithoutCRLF(out, derIssuerB64);
fputs("\n", out);
}
fprintf(out, "Serial DER Base64:\n");
if (SECU_GetWrapEnabled()) {
fprintf(out, "%s\n", derSerialB64);
} else {
printStringWithoutCRLF(out, derSerialB64);
fputs("\n", out);
}
PORT_Free(derIssuerB64);
PORT_Free(derSerialB64);
fprintf(out, "Serial DER as C source: \n{ %d, \"", c->serialNumber.len);
{
unsigned int i;
for (i = 0; i < c->serialNumber.len; ++i) {
unsigned char *chardata = (unsigned char *)(c->serialNumber.data);
unsigned char ch = *(chardata + i);
fprintf(out, "\\x%02x", ch);
}
fprintf(out, "\" }\n");
}
loser:
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
static SECStatus
secu_PrintX509InvalidDate(FILE *out, SECItem *value, char *msg, int level)
{
SECItem decodedValue;
SECStatus rv;
PRTime invalidTime;
char *formattedTime = NULL;
decodedValue.data = NULL;
rv = SEC_ASN1DecodeItem(NULL, &decodedValue,
SEC_ASN1_GET(SEC_GeneralizedTimeTemplate),
value);
if (rv == SECSuccess) {
rv = DER_GeneralizedTimeToTime(&invalidTime, &decodedValue);
if (rv == SECSuccess) {
formattedTime = CERT_GenTime2FormattedAscii(invalidTime, "%a %b %d %H:%M:%S %Y");
SECU_Indent(out, level + 1);
fprintf(out, "%s: %s\n", msg, formattedTime);
PORT_Free(formattedTime);
}
}
PORT_Free(decodedValue.data);
return (rv);
}
static SECStatus
PrintExtKeyUsageExtension(FILE *out, SECItem *value, char *msg, int level)
{