mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-16 04:20:32 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
3798 lines
101 KiB
C
3798 lines
101 KiB
C
/* 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
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
/*
|
|
** 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 <sys/stat.h>
|
|
#include <errno.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;
|
|
|
|
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" };
|
|
char *pw;
|
|
|
|
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:
|
|
sprintf(prompt, "Enter Password or Pin for \"%s\":",
|
|
PK11_GetTokenName(slot));
|
|
return SECU_GetPasswordString(NULL, prompt);
|
|
case PW_FROMFILE:
|
|
/* Instead of opening and closing the file every time, get the pw
|
|
* once, then keep it in memory (duh).
|
|
*/
|
|
pw = SECU_FilePasswd(slot, retry, pwdata->data);
|
|
pwdata->source = PW_PLAINTEXT;
|
|
pwdata->data = PL_strdup(pw);
|
|
/* it's already been dup'ed */
|
|
return pw;
|
|
case PW_EXTERNAL:
|
|
sprintf(prompt,
|
|
"Press Enter, then enter PIN for \"%s\" on external device.\n",
|
|
PK11_GetTokenName(slot));
|
|
(void) SECU_GetPasswordString(NULL, prompt);
|
|
/* 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 */
|
|
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_GetEnv("SSL_DIR");
|
|
if (!dir)
|
|
return NULL;
|
|
|
|
sprintf(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] == '/')
|
|
sprintf(path, "%s%s", dir, filename);
|
|
else
|
|
sprintf(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_GetEnv("HOME");
|
|
if (!home) home = "";
|
|
|
|
if (*home && home[strlen(home) - 1] == '/')
|
|
sprintf (buf, "%.900s%s", home, dir);
|
|
else
|
|
sprintf (buf, "%.900s/%s", home, dir);
|
|
} else {
|
|
sprintf(buf, "%.900s", base);
|
|
if (buf[strlen(buf) - 1] == '/')
|
|
buf[strlen(buf) - 1] = 0;
|
|
}
|
|
|
|
|
|
initted = PR_TRUE;
|
|
return buf;
|
|
}
|
|
|
|
/*Turn off SSL for now */
|
|
/* This gets called by SSL when server wants our cert & key */
|
|
int
|
|
SECU_GetClientAuthData(void *arg, PRFileDesc *fd,
|
|
struct CERTDistNamesStr *caNames,
|
|
struct CERTCertificateStr **pRetCert,
|
|
struct SECKEYPrivateKeyStr **pRetKey)
|
|
{
|
|
SECKEYPrivateKey *key;
|
|
CERTCertificate *cert;
|
|
int errsave;
|
|
|
|
if (arg == NULL) {
|
|
fprintf(stderr, "no key/cert name specified for client auth\n");
|
|
return -1;
|
|
}
|
|
cert = PK11_FindCertFromNickname(arg, NULL);
|
|
errsave = PORT_GetError();
|
|
if (!cert) {
|
|
if (errsave == SEC_ERROR_BAD_PASSWORD)
|
|
fprintf(stderr, "Bad password\n");
|
|
else if (errsave > 0)
|
|
fprintf(stderr, "Unable to read cert (error %d)\n", errsave);
|
|
else if (errsave == SEC_ERROR_BAD_DATABASE)
|
|
fprintf(stderr, "Unable to get cert from database (%d)\n", errsave);
|
|
else
|
|
fprintf(stderr, "SECKEY_FindKeyByName: internal error %d\n", errsave);
|
|
return -1;
|
|
}
|
|
|
|
key = PK11_FindKeyByAnyCert(arg,NULL);
|
|
if (!key) {
|
|
fprintf(stderr, "Unable to get key (%d)\n", PORT_GetError());
|
|
return -1;
|
|
}
|
|
|
|
|
|
*pRetCert = cert;
|
|
*pRetKey = key;
|
|
|
|
return 0;
|
|
}
|
|
|
|
SECStatus
|
|
SECU_ReadDERFromFile(SECItem *der, PRFileDesc *inFile, PRBool ascii,
|
|
PRBool warnOnPrivateKeyInAsciiFile)
|
|
{
|
|
SECStatus rv;
|
|
if (ascii) {
|
|
/* First convert ascii to binary */
|
|
SECItem filedata;
|
|
char *asc, *body;
|
|
|
|
/* Read in ascii data */
|
|
rv = SECU_FileToItem(&filedata, inFile);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
asc = (char *)filedata.data;
|
|
if (!asc) {
|
|
fprintf(stderr, "unable to read data from input file\n");
|
|
return SECFailure;
|
|
}
|
|
|
|
if (warnOnPrivateKeyInAsciiFile && strstr(asc, "PRIVATE KEY")) {
|
|
fprintf(stderr, "Warning: ignoring private key. Consider to use "
|
|
"pk12util.\n");
|
|
}
|
|
|
|
/* 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 {
|
|
/* need one additional byte for zero terminator */
|
|
rv = SECITEM_ReallocItemV2(NULL, &filedata, filedata.len+1);
|
|
if (rv != SECSuccess) {
|
|
PORT_Free(filedata.data);
|
|
return rv;
|
|
}
|
|
body = (char*)filedata.data;
|
|
body[filedata.len-1] = '\0';
|
|
}
|
|
|
|
/* 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
|
|
|
|
SECStatus
|
|
SECU_StripTagAndLength(SECItem *i)
|
|
{
|
|
unsigned int start;
|
|
|
|
if (!i || !i->data || i->len < 2) { /* must be at least tag and length */
|
|
return SECFailure;
|
|
}
|
|
start = ((i->data[1] & 0x80) ? (i->data[1] & 0x7f) + 2 : 2);
|
|
if (i->len < start) {
|
|
return SECFailure;
|
|
}
|
|
i->data += start;
|
|
i->len -= start;
|
|
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 = my;
|
|
|
|
if (tmp.data[1] & 0x80) {
|
|
unsigned int i;
|
|
unsigned int lenlen = tmp.data[1] & 0x7f;
|
|
if (lenlen > sizeof tmp.len)
|
|
break;
|
|
tmp.len = 0;
|
|
for (i=0; i < lenlen; i++) {
|
|
tmp.len = (tmp.len << 8) | tmp.data[2+i];
|
|
}
|
|
tmp.len += lenlen + 2;
|
|
} else {
|
|
tmp.len = tmp.data[1] + 2;
|
|
}
|
|
if (tmp.len > my.len) {
|
|
tmp.len = my.len;
|
|
}
|
|
my.data += tmp.len;
|
|
my.len -= tmp.len;
|
|
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 */
|
|
void
|
|
SECU_PrintEncodedObjectID(FILE *out, const SECItem *i, const char *m, int level)
|
|
{
|
|
SECItem my = *i;
|
|
if (SECSuccess == SECU_StripTagAndLength(&my))
|
|
SECU_PrintObjectID(out, &my, m, level);
|
|
}
|
|
|
|
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))
|
|
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 (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
|
|
secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level+1);
|
|
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 {
|
|
/* 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) {
|
|
sprintf(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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef NSS_DISABLE_ECC
|
|
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;
|
|
SECU_PrintObjectID(out, &curveOID, "Curve", level +1);
|
|
}
|
|
}
|
|
#endif /* NSS_DISABLE_ECC */
|
|
|
|
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;
|
|
|
|
#ifndef NSS_DISABLE_ECC
|
|
case ecKey:
|
|
secu_PrintECPublicKey(out, pk, "EC Public Key", level +1);
|
|
break;
|
|
#endif
|
|
|
|
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 c = *(chardata + i);
|
|
|
|
fprintf(out, "\\x%02x", c);
|
|
}
|
|
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)
|
|
{
|
|
CERTOidSequence *os;
|
|
SECItem **op;
|
|
|
|
os = CERT_DecodeOidSequence(value);
|
|
if( (CERTOidSequence *)NULL == os ) {
|
|
return SECFailure;
|
|
}
|
|
|
|
for( op = os->oids; *op; op++ ) {
|
|
SECU_PrintObjectID(out, *op, msg, level + 1);
|
|
}
|
|
CERT_DestroyOidSequence(os);
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
secu_PrintBasicConstraints(FILE *out, SECItem *value, char *msg, int level) {
|
|
CERTBasicConstraints constraints;
|
|
SECStatus rv;
|
|
|
|
SECU_Indent(out, level);
|
|
if (msg) {
|
|
fprintf(out,"%s: ",msg);
|
|
}
|
|
rv = CERT_DecodeBasicConstraintValue(&constraints,value);
|
|
if (rv == SECSuccess && constraints.isCA) {
|
|
if (constraints.pathLenConstraint >= 0) {
|
|
fprintf(out,"Is a CA with a maximum path length of %d.\n",
|
|
constraints.pathLenConstraint);
|
|
} else {
|
|
fprintf(out,"Is a CA with no maximum path length.\n");
|
|
}
|
|
} else {
|
|
fprintf(out,"Is not a CA.\n");
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static const char * const nsTypeBits[] = {
|
|
"SSL Client",
|
|
"SSL Server",
|
|
"S/MIME",
|
|
"Object Signing",
|
|
"Reserved",
|
|
"SSL CA",
|
|
"S/MIME CA",
|
|
"ObjectSigning CA"
|
|
};
|
|
|
|
/* NSCertType is merely a bit string whose bits are displayed symbolically */
|
|
static SECStatus
|
|
secu_PrintNSCertType(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
int unused;
|
|
int NS_Type;
|
|
int i;
|
|
int found = 0;
|
|
SECItem my = *value;
|
|
|
|
if ((my.data[0] != SEC_ASN1_BIT_STRING) ||
|
|
SECSuccess != SECU_StripTagAndLength(&my)) {
|
|
SECU_PrintAny(out, value, "Data", level);
|
|
return SECSuccess;
|
|
}
|
|
|
|
unused = (my.len == 2) ? (my.data[0] & 0x0f) : 0;
|
|
NS_Type = my.data[1] & (0xff << unused);
|
|
|
|
|
|
SECU_Indent(out, level);
|
|
if (msg) {
|
|
fprintf(out,"%s: ",msg);
|
|
} else {
|
|
fprintf(out,"Netscape Certificate Type: ");
|
|
}
|
|
for (i=0; i < 8; i++) {
|
|
if ( (0x80 >> i) & NS_Type) {
|
|
fprintf(out, "%c%s", (found ? ',' : '<'), nsTypeBits[i]);
|
|
found = 1;
|
|
}
|
|
}
|
|
fprintf(out, (found ? ">\n" : "none\n"));
|
|
return SECSuccess;
|
|
}
|
|
|
|
static const char * const usageBits[] = {
|
|
"Digital Signature", /* 0x80 */
|
|
"Non-Repudiation", /* 0x40 */
|
|
"Key Encipherment", /* 0x20 */
|
|
"Data Encipherment", /* 0x10 */
|
|
"Key Agreement", /* 0x08 */
|
|
"Certificate Signing", /* 0x04 */
|
|
"CRL Signing", /* 0x02 */
|
|
"Encipher Only", /* 0x01 */
|
|
"Decipher Only", /* 0x0080 */
|
|
NULL
|
|
};
|
|
|
|
/* X509KeyUsage is merely a bit string whose bits are displayed symbolically */
|
|
static void
|
|
secu_PrintX509KeyUsage(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
int unused;
|
|
int usage;
|
|
int i;
|
|
int found = 0;
|
|
SECItem my = *value;
|
|
|
|
if ((my.data[0] != SEC_ASN1_BIT_STRING) ||
|
|
SECSuccess != SECU_StripTagAndLength(&my)) {
|
|
SECU_PrintAny(out, value, "Data", level);
|
|
return;
|
|
}
|
|
|
|
unused = (my.len >= 2) ? (my.data[0] & 0x0f) : 0;
|
|
usage = (my.len == 2) ? (my.data[1] & (0xff << unused)) << 8
|
|
: (my.data[1] << 8) |
|
|
(my.data[2] & (0xff << unused));
|
|
|
|
SECU_Indent(out, level);
|
|
fprintf(out, "Usages: ");
|
|
for (i=0; usageBits[i]; i++) {
|
|
if ( (0x8000 >> i) & usage) {
|
|
if (found)
|
|
SECU_Indent(out, level + 2);
|
|
fprintf(out, "%s\n", usageBits[i]);
|
|
found = 1;
|
|
}
|
|
}
|
|
if (!found) {
|
|
fprintf(out, "(none)\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
secu_PrintIPAddress(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
PRStatus st;
|
|
PRNetAddr addr;
|
|
char addrBuf[80];
|
|
|
|
memset(&addr, 0, sizeof addr);
|
|
if (value->len == 4) {
|
|
addr.inet.family = PR_AF_INET;
|
|
memcpy(&addr.inet.ip, value->data, value->len);
|
|
} else if (value->len == 16) {
|
|
addr.ipv6.family = PR_AF_INET6;
|
|
memcpy(addr.ipv6.ip.pr_s6_addr, value->data, value->len);
|
|
if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped)) {
|
|
/* convert to IPv4. */
|
|
addr.inet.family = PR_AF_INET;
|
|
memcpy(&addr.inet.ip, &addr.ipv6.ip.pr_s6_addr[12], 4);
|
|
memset(&addr.inet.pad[0], 0, sizeof addr.inet.pad);
|
|
}
|
|
} else {
|
|
goto loser;
|
|
}
|
|
|
|
st = PR_NetAddrToString(&addr, addrBuf, sizeof addrBuf);
|
|
if (st == PR_SUCCESS) {
|
|
SECU_Indent(out, level);
|
|
fprintf(out, "%s: %s\n", msg, addrBuf);
|
|
} else {
|
|
loser:
|
|
SECU_PrintAsHex(out, value, msg, level);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
secu_PrintGeneralName(FILE *out, CERTGeneralName *gname, char *msg, int level)
|
|
{
|
|
char label[40];
|
|
if (msg && msg[0]) {
|
|
SECU_Indent(out, level++); fprintf(out, "%s: \n", msg);
|
|
}
|
|
switch (gname->type) {
|
|
case certOtherName :
|
|
SECU_PrintAny( out, &gname->name.OthName.name, "Other Name", level);
|
|
SECU_PrintObjectID(out, &gname->name.OthName.oid, "OID", level+1);
|
|
break;
|
|
case certDirectoryName :
|
|
SECU_PrintName(out, &gname->name.directoryName, "Directory Name", level);
|
|
break;
|
|
case certRFC822Name :
|
|
secu_PrintRawString( out, &gname->name.other, "RFC822 Name", level);
|
|
break;
|
|
case certDNSName :
|
|
secu_PrintRawString( out, &gname->name.other, "DNS name", level);
|
|
break;
|
|
case certURI :
|
|
secu_PrintRawString( out, &gname->name.other, "URI", level);
|
|
break;
|
|
case certIPAddress :
|
|
secu_PrintIPAddress(out, &gname->name.other, "IP Address", level);
|
|
break;
|
|
case certRegisterID :
|
|
SECU_PrintObjectID( out, &gname->name.other, "Registered ID", level);
|
|
break;
|
|
case certX400Address :
|
|
SECU_PrintAny( out, &gname->name.other, "X400 Address", level);
|
|
break;
|
|
case certEDIPartyName :
|
|
SECU_PrintAny( out, &gname->name.other, "EDI Party", level);
|
|
break;
|
|
default:
|
|
PR_snprintf(label, sizeof label, "unknown type [%d]",
|
|
(int)gname->type - 1);
|
|
SECU_PrintAsHex(out, &gname->name.other, label, level);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
secu_PrintGeneralNames(FILE *out, CERTGeneralName *gname, char *msg, int level)
|
|
{
|
|
CERTGeneralName *name = gname;
|
|
do {
|
|
secu_PrintGeneralName(out, name, msg, level);
|
|
name = CERT_GetNextGeneralName(name);
|
|
} while (name && name != gname);
|
|
}
|
|
|
|
|
|
static void
|
|
secu_PrintAuthKeyIDExtension(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
CERTAuthKeyID *kid = NULL;
|
|
PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if (!pool) {
|
|
SECU_PrintError("Error", "Allocating new ArenaPool");
|
|
return;
|
|
}
|
|
kid = CERT_DecodeAuthKeyID(pool, value);
|
|
if (!kid) {
|
|
SECU_PrintErrMsg(out, level, "Error", "Parsing extension");
|
|
SECU_PrintAny(out, value, "Data", level);
|
|
} else {
|
|
int keyIDPresent = (kid->keyID.data && kid->keyID.len);
|
|
int issuerPresent = kid->authCertIssuer != NULL;
|
|
int snPresent = (kid->authCertSerialNumber.data &&
|
|
kid->authCertSerialNumber.len);
|
|
|
|
if (keyIDPresent)
|
|
SECU_PrintAsHex(out, &kid->keyID, "Key ID", level);
|
|
if (issuerPresent)
|
|
secu_PrintGeneralName(out, kid->authCertIssuer, "Issuer", level);
|
|
if (snPresent)
|
|
SECU_PrintInteger(out, &kid->authCertSerialNumber,
|
|
"Serial Number", level);
|
|
}
|
|
PORT_FreeArena(pool, PR_FALSE);
|
|
}
|
|
|
|
|
|
static void
|
|
secu_PrintAltNameExtension(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
CERTGeneralName * nameList;
|
|
CERTGeneralName * current;
|
|
PLArenaPool * pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if (!pool) {
|
|
SECU_PrintError("Error", "Allocating new ArenaPool");
|
|
return;
|
|
}
|
|
nameList = current = CERT_DecodeAltNameExtension(pool, value);
|
|
if (!current) {
|
|
if (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) {
|
|
/* Decoder found empty sequence, which is invalid. */
|
|
PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID);
|
|
}
|
|
SECU_PrintErrMsg(out, level, "Error", "Parsing extension");
|
|
SECU_PrintAny(out, value, "Data", level);
|
|
} else {
|
|
do {
|
|
secu_PrintGeneralName(out, current, msg, level);
|
|
current = CERT_GetNextGeneralName(current);
|
|
} while (current != nameList);
|
|
}
|
|
PORT_FreeArena(pool, PR_FALSE);
|
|
}
|
|
|
|
static void
|
|
secu_PrintCRLDistPtsExtension(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
CERTCrlDistributionPoints * dPoints;
|
|
PLArenaPool * pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if (!pool) {
|
|
SECU_PrintError("Error", "Allocating new ArenaPool");
|
|
return;
|
|
}
|
|
dPoints = CERT_DecodeCRLDistributionPoints(pool, value);
|
|
if (dPoints && dPoints->distPoints && dPoints->distPoints[0]) {
|
|
CRLDistributionPoint ** pPoints = dPoints->distPoints;
|
|
CRLDistributionPoint * pPoint;
|
|
while (NULL != (pPoint = *pPoints++)) {
|
|
SECU_Indent(out, level); fputs("Distribution point:\n", out);
|
|
if (pPoint->distPointType == generalName &&
|
|
pPoint->distPoint.fullName != NULL) {
|
|
secu_PrintGeneralNames(out, pPoint->distPoint.fullName, NULL,
|
|
level + 1);
|
|
} else if (pPoint->distPointType == relativeDistinguishedName &&
|
|
pPoint->distPoint.relativeName.avas) {
|
|
SECU_PrintRDN(out, &pPoint->distPoint.relativeName, "RDN",
|
|
level + 1);
|
|
} else if (pPoint->derDistPoint.data) {
|
|
SECU_PrintAny(out, &pPoint->derDistPoint, "Point", level + 1);
|
|
}
|
|
if (pPoint->reasons.data) {
|
|
secu_PrintDecodedBitString(out, &pPoint->reasons, "Reasons",
|
|
level + 1);
|
|
}
|
|
if (pPoint->crlIssuer) {
|
|
secu_PrintGeneralName(out, pPoint->crlIssuer, "CRL issuer",
|
|
level + 1);
|
|
}
|
|
}
|
|
} else {
|
|
SECU_PrintErrMsg(out, level, "Error", "Parsing extension");
|
|
SECU_PrintAny(out, value, "Data", level);
|
|
}
|
|
PORT_FreeArena(pool, PR_FALSE);
|
|
}
|
|
|
|
|
|
static void
|
|
secu_PrintNameConstraintSubtree(FILE *out, CERTNameConstraint *value,
|
|
char *msg, int level)
|
|
{
|
|
CERTNameConstraint *head = value;
|
|
SECU_Indent(out, level); fprintf(out, "%s Subtree:\n", msg);
|
|
level++;
|
|
do {
|
|
secu_PrintGeneralName(out, &value->name, NULL, level);
|
|
if (value->min.data)
|
|
SECU_PrintInteger(out, &value->min, "Minimum", level+1);
|
|
if (value->max.data)
|
|
SECU_PrintInteger(out, &value->max, "Maximum", level+1);
|
|
value = CERT_GetNextNameConstraint(value);
|
|
} while (value != head);
|
|
}
|
|
|
|
static void
|
|
secu_PrintNameConstraintsExtension(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
CERTNameConstraints * cnstrnts;
|
|
PLArenaPool * pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if (!pool) {
|
|
SECU_PrintError("Error", "Allocating new ArenaPool");
|
|
return;
|
|
}
|
|
cnstrnts = CERT_DecodeNameConstraintsExtension(pool, value);
|
|
if (!cnstrnts) {
|
|
SECU_PrintErrMsg(out, level, "Error", "Parsing extension");
|
|
SECU_PrintAny(out, value, "Raw", level);
|
|
} else {
|
|
if (cnstrnts->permited)
|
|
secu_PrintNameConstraintSubtree(out, cnstrnts->permited,
|
|
"Permitted", level);
|
|
if (cnstrnts->excluded)
|
|
secu_PrintNameConstraintSubtree(out, cnstrnts->excluded,
|
|
"Excluded", level);
|
|
}
|
|
PORT_FreeArena(pool, PR_FALSE);
|
|
}
|
|
|
|
|
|
static void
|
|
secu_PrintAuthorityInfoAcess(FILE *out, SECItem *value, char *msg, int level)
|
|
{
|
|
CERTAuthInfoAccess **infos = NULL;
|
|
PLArenaPool * pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
|
|
if (!pool) {
|
|
SECU_PrintError("Error", "Allocating new ArenaPool");
|
|
return;
|
|
}
|
|
infos = CERT_DecodeAuthInfoAccessExtension(pool, value);
|
|
if (!infos) {
|
|
SECU_PrintErrMsg(out, level, "Error", "Parsing extension");
|
|
SECU_PrintAny(out, value, "Raw", level);
|
|
} else {
|
|
CERTAuthInfoAccess *info;
|
|
while (NULL != (info = *infos++)) {
|
|
if (info->method.data) {
|
|
SECU_PrintObjectID(out, &info->method, "Method", level);
|
|
} else {
|
|
SECU_Indent(out,level);
|
|
fprintf(out, "Error: missing method\n");
|
|
}
|
|
if (info->location) {
|
|
secu_PrintGeneralName(out, info->location, "Location", level);
|
|
} else {
|
|
SECU_PrintAny(out, &info->derLocation, "Location", level);
|
|
}
|
|
}
|
|
}
|
|
PORT_FreeArena(pool, PR_FALSE);
|
|
}
|
|
|
|
|
|
void
|
|
SECU_PrintExtensions(FILE *out, CERTCertExtension **extensions,
|
|
char *msg, int level)
|
|
{
|
|
SECOidTag oidTag;
|
|
|
|
if ( extensions ) {
|
|
if (msg && *msg) {
|
|
SECU_Indent(out, level++); fprintf(out, "%s:\n", msg);
|
|
}
|
|
|
|
while ( *extensions ) {
|
|
SECItem *tmpitem;
|
|
|
|
tmpitem = &(*extensions)->id;
|
|
SECU_PrintObjectID(out, tmpitem, "Name", level);
|
|
|
|
tmpitem = &(*extensions)->critical;
|
|
if ( tmpitem->len ) {
|
|
secu_PrintBoolean(out, tmpitem, "Critical", level);
|
|
}
|
|
|
|
oidTag = SECOID_FindOIDTag (&((*extensions)->id));
|
|
tmpitem = &((*extensions)->value);
|
|
|
|
switch (oidTag) {
|
|
case SEC_OID_X509_INVALID_DATE:
|
|
case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_TIME:
|
|
secu_PrintX509InvalidDate(out, tmpitem, "Date", level );
|
|
break;
|
|
case SEC_OID_X509_CERTIFICATE_POLICIES:
|
|
SECU_PrintPolicy(out, tmpitem, "Data", level );
|
|
break;
|
|
case SEC_OID_NS_CERT_EXT_BASE_URL:
|
|
case SEC_OID_NS_CERT_EXT_REVOCATION_URL:
|
|
case SEC_OID_NS_CERT_EXT_CA_REVOCATION_URL:
|
|
case SEC_OID_NS_CERT_EXT_CA_CRL_URL:
|
|
case SEC_OID_NS_CERT_EXT_CA_CERT_URL:
|
|
case SEC_OID_NS_CERT_EXT_CERT_RENEWAL_URL:
|
|
case SEC_OID_NS_CERT_EXT_CA_POLICY_URL:
|
|
case SEC_OID_NS_CERT_EXT_HOMEPAGE_URL:
|
|
case SEC_OID_NS_CERT_EXT_LOST_PASSWORD_URL:
|
|
case SEC_OID_OCSP_RESPONDER:
|
|
SECU_PrintString(out,tmpitem, "URL", level);
|
|
break;
|
|
case SEC_OID_NS_CERT_EXT_COMMENT:
|
|
SECU_PrintString(out,tmpitem, "Comment", level);
|
|
break;
|
|
case SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME:
|
|
SECU_PrintString(out,tmpitem, "ServerName", level);
|
|
break;
|
|
case SEC_OID_NS_CERT_EXT_CERT_TYPE:
|
|
secu_PrintNSCertType(out,tmpitem,"Data",level);
|
|
break;
|
|
case SEC_OID_X509_BASIC_CONSTRAINTS:
|
|
secu_PrintBasicConstraints(out,tmpitem,"Data",level);
|
|
break;
|
|
case SEC_OID_X509_EXT_KEY_USAGE:
|
|
PrintExtKeyUsageExtension(out, tmpitem, NULL, level);
|
|
break;
|
|
case SEC_OID_X509_KEY_USAGE:
|
|
secu_PrintX509KeyUsage(out, tmpitem, NULL, level );
|
|
break;
|
|
case SEC_OID_X509_AUTH_KEY_ID:
|
|
secu_PrintAuthKeyIDExtension(out, tmpitem, NULL, level );
|
|
break;
|
|
case SEC_OID_X509_SUBJECT_ALT_NAME:
|
|
case SEC_OID_X509_ISSUER_ALT_NAME:
|
|
secu_PrintAltNameExtension(out, tmpitem, NULL, level );
|
|
break;
|
|
case SEC_OID_X509_CRL_DIST_POINTS:
|
|
secu_PrintCRLDistPtsExtension(out, tmpitem, NULL, level );
|
|
break;
|
|
case SEC_OID_X509_PRIVATE_KEY_USAGE_PERIOD:
|
|
SECU_PrintPrivKeyUsagePeriodExtension(out, tmpitem, NULL,
|
|
level );
|
|
break;
|
|
case SEC_OID_X509_NAME_CONSTRAINTS:
|
|
secu_PrintNameConstraintsExtension(out, tmpitem, NULL, level);
|
|
break;
|
|
case SEC_OID_X509_AUTH_INFO_ACCESS:
|
|
secu_PrintAuthorityInfoAcess(out, tmpitem, NULL, level);
|
|
break;
|
|
|
|
case SEC_OID_X509_CRL_NUMBER:
|
|
case SEC_OID_X509_REASON_CODE:
|
|
|
|
/* PKIX OIDs */
|
|
case SEC_OID_PKIX_OCSP:
|
|
case SEC_OID_PKIX_OCSP_BASIC_RESPONSE:
|
|
case SEC_OID_PKIX_OCSP_NONCE:
|
|
case SEC_OID_PKIX_OCSP_CRL:
|
|
case SEC_OID_PKIX_OCSP_RESPONSE:
|
|
case SEC_OID_PKIX_OCSP_NO_CHECK:
|
|
case SEC_OID_PKIX_OCSP_ARCHIVE_CUTOFF:
|
|
case SEC_OID_PKIX_OCSP_SERVICE_LOCATOR:
|
|
case SEC_OID_PKIX_REGCTRL_REGTOKEN:
|
|
case SEC_OID_PKIX_REGCTRL_AUTHENTICATOR:
|
|
case SEC_OID_PKIX_REGCTRL_PKIPUBINFO:
|
|
case SEC_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS:
|
|
case SEC_OID_PKIX_REGCTRL_OLD_CERT_ID:
|
|
case SEC_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY:
|
|
case SEC_OID_PKIX_REGINFO_UTF8_PAIRS:
|
|
case SEC_OID_PKIX_REGINFO_CERT_REQUEST:
|
|
|
|
/* Netscape extension OIDs. */
|
|
case SEC_OID_NS_CERT_EXT_NETSCAPE_OK:
|
|
case SEC_OID_NS_CERT_EXT_ISSUER_LOGO:
|
|
case SEC_OID_NS_CERT_EXT_SUBJECT_LOGO:
|
|
case SEC_OID_NS_CERT_EXT_ENTITY_LOGO:
|
|
case SEC_OID_NS_CERT_EXT_USER_PICTURE:
|
|
|
|
/* x.509 v3 Extensions */
|
|
case SEC_OID_X509_SUBJECT_DIRECTORY_ATTR:
|
|
case SEC_OID_X509_SUBJECT_KEY_ID:
|
|
case SEC_OID_X509_POLICY_MAPPINGS:
|
|
case SEC_OID_X509_POLICY_CONSTRAINTS:
|
|
|
|
|
|
default:
|
|
SECU_PrintAny(out, tmpitem, "Data", level);
|
|
break;
|
|
}
|
|
|
|
SECU_Newline(out);
|
|
extensions++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* An RDN is a subset of a DirectoryName, and we already know how to
|
|
* print those, so make a directory name out of the RDN, and print it.
|
|
*/
|
|
void
|
|
SECU_PrintRDN(FILE *out, CERTRDN *rdn, const char *msg, int level)
|
|
{
|
|
CERTName name;
|
|
CERTRDN *rdns[2];
|
|
|
|
name.arena = NULL;
|
|
name.rdns = rdns;
|
|
rdns[0] = rdn;
|
|
rdns[1] = NULL;
|
|
SECU_PrintName(out, &name, msg, level);
|
|
}
|
|
|
|
void
|
|
SECU_PrintNameQuotesOptional(FILE *out, CERTName *name, const char *msg,
|
|
int level, PRBool quotes)
|
|
{
|
|
char *nameStr = NULL;
|
|
char *str;
|
|
SECItem my;
|
|
|
|
if (!name) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return;
|
|
}
|
|
if (!name->rdns || !name->rdns[0]) {
|
|
str = "(empty)";
|
|
} else {
|
|
str = nameStr = CERT_NameToAscii(name);
|
|
}
|
|
if (!str) {
|
|
str = "!Invalid AVA!";
|
|
}
|
|
my.data = (unsigned char *)str;
|
|
my.len = PORT_Strlen(str);
|
|
#if 1
|
|
secu_PrintRawStringQuotesOptional(out, &my, msg, level, quotes);
|
|
#else
|
|
SECU_Indent(out, level); fprintf(out, "%s: ", msg);
|
|
fprintf(out, str);
|
|
SECU_Newline(out);
|
|
#endif
|
|
PORT_Free(nameStr);
|
|
}
|
|
|
|
void
|
|
SECU_PrintName(FILE *out, CERTName *name, const char *msg, int level)
|
|
{
|
|
SECU_PrintNameQuotesOptional(out, name, msg, level, PR_TRUE);
|
|
}
|
|
|
|
void
|
|
printflags(char *trusts, unsigned int flags)
|
|
{
|
|
if (flags & CERTDB_VALID_CA)
|
|
if (!(flags & CERTDB_TRUSTED_CA) &&
|
|
!(flags & CERTDB_TRUSTED_CLIENT_CA))
|
|
PORT_Strcat(trusts, "c");
|
|
if (flags & CERTDB_TERMINAL_RECORD)
|
|
if (!(flags & CERTDB_TRUSTED))
|
|
PORT_Strcat(trusts, "p");
|
|
if (flags & CERTDB_TRUSTED_CA)
|
|
PORT_Strcat(trusts, "C");
|
|
if (flags & CERTDB_TRUSTED_CLIENT_CA)
|
|
PORT_Strcat(trusts, "T");
|
|
if (flags & CERTDB_TRUSTED)
|
|
PORT_Strcat(trusts, "P");
|
|
if (flags & CERTDB_USER)
|
|
PORT_Strcat(trusts, "u");
|
|
if (flags & CERTDB_SEND_WARN)
|
|
PORT_Strcat(trusts, "w");
|
|
if (flags & CERTDB_INVISIBLE_CA)
|
|
PORT_Strcat(trusts, "I");
|
|
if (flags & CERTDB_GOVT_APPROVED_CA)
|
|
PORT_Strcat(trusts, "G");
|
|
return;
|
|
}
|
|
|
|
/* callback for listing certs through pkcs11 */
|
|
SECStatus
|
|
SECU_PrintCertNickname(CERTCertListNode *node, void *data)
|
|
{
|
|
CERTCertTrust trust;
|
|
CERTCertificate* cert;
|
|
FILE *out;
|
|
char trusts[30];
|
|
char *name;
|
|
|
|
cert = node->cert;
|
|
|
|
PORT_Memset (trusts, 0, sizeof (trusts));
|
|
out = (FILE *)data;
|
|
|
|
name = node->appData;
|
|
if (!name || !name[0]) {
|
|
name = cert->nickname;
|
|
}
|
|
if (!name || !name[0]) {
|
|
name = cert->emailAddr;
|
|
}
|
|
if (!name || !name[0]) {
|
|
name = "(NULL)";
|
|
}
|
|
|
|
if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
|
|
printflags(trusts, trust.sslFlags);
|
|
PORT_Strcat(trusts, ",");
|
|
printflags(trusts, trust.emailFlags);
|
|
PORT_Strcat(trusts, ",");
|
|
printflags(trusts, trust.objectSigningFlags);
|
|
} else {
|
|
PORT_Memcpy(trusts,",,",3);
|
|
}
|
|
fprintf(out, "%-60s %-5s\n", name, trusts);
|
|
|
|
return (SECSuccess);
|
|
}
|
|
|
|
int
|
|
SECU_DecodeAndPrintExtensions(FILE *out, SECItem *any, char *m, int level)
|
|
{
|
|
CERTCertExtension **extensions = NULL;
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
int rv = 0;
|
|
|
|
if (!arena)
|
|
return SEC_ERROR_NO_MEMORY;
|
|
|
|
rv = SEC_QuickDERDecodeItem(arena, &extensions,
|
|
SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate), any);
|
|
if (!rv)
|
|
SECU_PrintExtensions(out, extensions, m, level);
|
|
else
|
|
SECU_PrintAny(out, any, m, level);
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
/* print a decoded SET OF or SEQUENCE OF Extensions */
|
|
int
|
|
SECU_PrintSetOfExtensions(FILE *out, SECItem **any, char *m, int level)
|
|
{
|
|
int rv = 0;
|
|
if (m && *m) {
|
|
SECU_Indent(out, level++); fprintf(out, "%s:\n", m);
|
|
}
|
|
while (any && any[0]) {
|
|
rv |= SECU_DecodeAndPrintExtensions(out, any[0], "", level);
|
|
any++;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/* print a decoded SET OF or SEQUENCE OF "ANY" */
|
|
int
|
|
SECU_PrintSetOfAny(FILE *out, SECItem **any, char *m, int level)
|
|
{
|
|
int rv = 0;
|
|
if (m && *m) {
|
|
SECU_Indent(out, level++); fprintf(out, "%s:\n", m);
|
|
}
|
|
while (any && any[0]) {
|
|
SECU_PrintAny(out, any[0], "", level);
|
|
any++;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
SECU_PrintCertAttribute(FILE *out, CERTAttribute *attr, char *m, int level)
|
|
{
|
|
int rv = 0;
|
|
SECOidTag tag;
|
|
tag = SECU_PrintObjectID(out, &attr->attrType, "Attribute Type", level);
|
|
if (tag == SEC_OID_PKCS9_EXTENSION_REQUEST) {
|
|
rv = SECU_PrintSetOfExtensions(out, attr->attrValue, "Extensions", level);
|
|
} else {
|
|
rv = SECU_PrintSetOfAny(out, attr->attrValue, "Attribute Values", level);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
SECU_PrintCertAttributes(FILE *out, CERTAttribute **attrs, char *m, int level)
|
|
{
|
|
int rv = 0;
|
|
while (attrs[0]) {
|
|
rv |= SECU_PrintCertAttribute(out, attrs[0], m, level+1);
|
|
attrs++;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int /* sometimes a PRErrorCode, other times a SECStatus. Sigh. */
|
|
SECU_PrintCertificateRequest(FILE *out, SECItem *der, char *m, int level)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
CERTCertificateRequest *cr;
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
|
|
if (!arena)
|
|
return rv;
|
|
|
|
/* Decode certificate request */
|
|
cr = PORT_ArenaZNew(arena, CERTCertificateRequest);
|
|
if (!cr)
|
|
goto loser;
|
|
cr->arena = arena;
|
|
rv = SEC_QuickDERDecodeItem(arena, cr,
|
|
SEC_ASN1_GET(CERT_CertificateRequestTemplate), der);
|
|
if (rv)
|
|
goto loser;
|
|
|
|
/* Pretty print it out */
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &cr->version, "Version", level+1);
|
|
SECU_PrintName(out, &cr->subject, "Subject", level+1);
|
|
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
|
|
SECU_Newline(out);
|
|
secu_PrintSubjectPublicKeyInfo(out, arena, &cr->subjectPublicKeyInfo,
|
|
"Subject Public Key Info", level+1);
|
|
if (cr->attributes)
|
|
SECU_PrintCertAttributes(out, cr->attributes, "Attributes", level+1);
|
|
rv = 0;
|
|
loser:
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
SECU_PrintCertificate(FILE *out, const SECItem *der, const char *m, int level)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
CERTCertificate *c;
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
int iv;
|
|
|
|
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_Indent(out, level);
|
|
SECU_PrintErrMsg(out, level, "Error", "Parsing extension");
|
|
SECU_PrintAny(out, der, "Raw", level);
|
|
goto loser;
|
|
}
|
|
/* Pretty print it out */
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
iv = c->version.len ? DER_GetInteger(&c->version) : 0; /* version is optional */
|
|
SECU_Indent(out, level+1); fprintf(out, "%s: %d (0x%x)\n", "Version", iv + 1, iv);
|
|
|
|
SECU_PrintInteger(out, &c->serialNumber, "Serial Number", level+1);
|
|
SECU_PrintAlgorithmID(out, &c->signature, "Signature Algorithm", level+1);
|
|
SECU_PrintName(out, &c->issuer, "Issuer", level+1);
|
|
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
|
|
SECU_Newline(out);
|
|
secu_PrintValidity(out, &c->validity, "Validity", level+1);
|
|
SECU_PrintName(out, &c->subject, "Subject", level+1);
|
|
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
|
|
SECU_Newline(out);
|
|
secu_PrintSubjectPublicKeyInfo(out, arena, &c->subjectPublicKeyInfo,
|
|
"Subject Public Key Info", level+1);
|
|
if (c->issuerID.data)
|
|
secu_PrintDecodedBitString(out, &c->issuerID, "Issuer Unique ID", level+1);
|
|
if (c->subjectID.data)
|
|
secu_PrintDecodedBitString(out, &c->subjectID, "Subject Unique ID", level+1);
|
|
SECU_PrintExtensions(out, c->extensions, "Signed Extensions", level+1);
|
|
loser:
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
SECU_PrintCertificateBasicInfo(FILE *out, const SECItem *der, const char *m, int level)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
CERTCertificate *c;
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
|
|
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_Indent(out, level);
|
|
SECU_PrintErrMsg(out, level, "Error", "Parsing extension");
|
|
SECU_PrintAny(out, der, "Raw", level);
|
|
goto loser;
|
|
}
|
|
/* Pretty print it out */
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &c->serialNumber, "Serial Number", level+1);
|
|
SECU_PrintAlgorithmID(out, &c->signature, "Signature Algorithm", level+1);
|
|
SECU_PrintName(out, &c->issuer, "Issuer", level+1);
|
|
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
|
|
SECU_Newline(out);
|
|
secu_PrintValidity(out, &c->validity, "Validity", level+1);
|
|
SECU_PrintName(out, &c->subject, "Subject", level+1);
|
|
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
|
|
SECU_Newline(out);
|
|
loser:
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
SECU_PrintSubjectPublicKeyInfo(FILE *out, SECItem *der, char *m, int level)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
CERTSubjectPublicKeyInfo spki;
|
|
|
|
if (!arena)
|
|
return rv;
|
|
|
|
PORT_Memset(&spki, 0, sizeof spki);
|
|
rv = SEC_ASN1DecodeItem(arena, &spki,
|
|
SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate),
|
|
der);
|
|
if (!rv) {
|
|
if (m && *m) {
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
}
|
|
secu_PrintSubjectPublicKeyInfo(out, arena, &spki,
|
|
"Subject Public Key Info", level+1);
|
|
}
|
|
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
#ifdef HAVE_EPV_TEMPLATE
|
|
int
|
|
SECU_PrintPrivateKey(FILE *out, SECItem *der, char *m, int level)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
SECKEYEncryptedPrivateKeyInfo key;
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
|
|
if (!arena)
|
|
return rv;
|
|
|
|
PORT_Memset(&key, 0, sizeof(key));
|
|
rv = SEC_ASN1DecodeItem(arena, &key,
|
|
SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate), der);
|
|
if (rv)
|
|
goto loser;
|
|
|
|
/* Pretty print it out */
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintAlgorithmID(out, &key.algorithm, "Encryption Algorithm",
|
|
level+1);
|
|
SECU_PrintAsHex(out, &key.encryptedData, "Encrypted Data", level+1);
|
|
loser:
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
SECU_PrintFingerprints(FILE *out, SECItem *derCert, char *m, int level)
|
|
{
|
|
unsigned char fingerprint[SHA256_LENGTH];
|
|
char *fpStr = NULL;
|
|
int err = PORT_GetError();
|
|
SECStatus rv;
|
|
SECItem fpItem;
|
|
|
|
/* Print SHA-256 fingerprint */
|
|
memset(fingerprint, 0, sizeof fingerprint);
|
|
rv = PK11_HashBuf(SEC_OID_SHA256, fingerprint, derCert->data, derCert->len);
|
|
fpItem.data = fingerprint;
|
|
fpItem.len = SHA256_LENGTH;
|
|
fpStr = CERT_Hexify(&fpItem, 1);
|
|
SECU_Indent(out, level); fprintf(out, "%s (SHA-256):", m);
|
|
if (SECU_GetWrapEnabled()) {
|
|
fprintf(out, "\n");
|
|
SECU_Indent(out, level+1);
|
|
}
|
|
else {
|
|
fprintf(out, " ");
|
|
}
|
|
fprintf(out, "%s\n", fpStr);
|
|
PORT_Free(fpStr);
|
|
fpStr = NULL;
|
|
if (rv != SECSuccess && !err)
|
|
err = PORT_GetError();
|
|
|
|
/* print SHA1 fingerprint */
|
|
memset(fingerprint, 0, sizeof fingerprint);
|
|
rv = PK11_HashBuf(SEC_OID_SHA1,fingerprint, derCert->data, derCert->len);
|
|
fpItem.data = fingerprint;
|
|
fpItem.len = SHA1_LENGTH;
|
|
fpStr = CERT_Hexify(&fpItem, 1);
|
|
SECU_Indent(out, level); fprintf(out, "%s (SHA1):", m);
|
|
if (SECU_GetWrapEnabled()) {
|
|
fprintf(out, "\n");
|
|
SECU_Indent(out, level+1);
|
|
}
|
|
else {
|
|
fprintf(out, " ");
|
|
}
|
|
fprintf(out, "%s\n", fpStr);
|
|
PORT_Free(fpStr);
|
|
if (SECU_GetWrapEnabled())
|
|
fprintf(out, "\n");
|
|
|
|
if (err)
|
|
PORT_SetError(err);
|
|
if (err || rv != SECSuccess)
|
|
return SECFailure;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** PKCS7 Support
|
|
*/
|
|
|
|
/* forward declaration */
|
|
static int
|
|
secu_PrintPKCS7ContentInfo(FILE *, SEC_PKCS7ContentInfo *, char *, int);
|
|
|
|
/*
|
|
** secu_PrintPKCS7EncContent
|
|
** Prints a SEC_PKCS7EncryptedContentInfo (without decrypting it)
|
|
*/
|
|
static void
|
|
secu_PrintPKCS7EncContent(FILE *out, SEC_PKCS7EncryptedContentInfo *src,
|
|
char *m, int level)
|
|
{
|
|
if (src->contentTypeTag == NULL)
|
|
src->contentTypeTag = SECOID_FindOID(&(src->contentType));
|
|
|
|
SECU_Indent(out, level);
|
|
fprintf(out, "%s:\n", m);
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Content Type: %s\n",
|
|
(src->contentTypeTag != NULL) ? src->contentTypeTag->desc
|
|
: "Unknown");
|
|
SECU_PrintAlgorithmID(out, &(src->contentEncAlg),
|
|
"Content Encryption Algorithm", level+1);
|
|
SECU_PrintAsHex(out, &(src->encContent),
|
|
"Encrypted Content", level+1);
|
|
}
|
|
|
|
/*
|
|
** secu_PrintRecipientInfo
|
|
** Prints a PKCS7RecipientInfo type
|
|
*/
|
|
static void
|
|
secu_PrintRecipientInfo(FILE *out, SEC_PKCS7RecipientInfo *info, char *m,
|
|
int level)
|
|
{
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &(info->version), "Version", level + 1);
|
|
|
|
SECU_PrintName(out, &(info->issuerAndSN->issuer), "Issuer",
|
|
level + 1);
|
|
SECU_PrintInteger(out, &(info->issuerAndSN->serialNumber),
|
|
"Serial Number", level + 1);
|
|
|
|
/* Parse and display encrypted key */
|
|
SECU_PrintAlgorithmID(out, &(info->keyEncAlg),
|
|
"Key Encryption Algorithm", level + 1);
|
|
SECU_PrintAsHex(out, &(info->encKey), "Encrypted Key", level + 1);
|
|
}
|
|
|
|
/*
|
|
** secu_PrintSignerInfo
|
|
** Prints a PKCS7SingerInfo type
|
|
*/
|
|
static void
|
|
secu_PrintSignerInfo(FILE *out, SEC_PKCS7SignerInfo *info, char *m, int level)
|
|
{
|
|
SEC_PKCS7Attribute *attr;
|
|
int iv;
|
|
char om[100];
|
|
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &(info->version), "Version", level + 1);
|
|
|
|
SECU_PrintName(out, &(info->issuerAndSN->issuer), "Issuer",
|
|
level + 1);
|
|
SECU_PrintInteger(out, &(info->issuerAndSN->serialNumber),
|
|
"Serial Number", level + 1);
|
|
|
|
SECU_PrintAlgorithmID(out, &(info->digestAlg), "Digest Algorithm",
|
|
level + 1);
|
|
|
|
if (info->authAttr != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Authenticated Attributes:\n");
|
|
iv = 0;
|
|
while ((attr = info->authAttr[iv++]) != NULL) {
|
|
sprintf(om, "Attribute (%d)", iv);
|
|
secu_PrintAttribute(out, attr, om, level + 2);
|
|
}
|
|
}
|
|
|
|
/* Parse and display signature */
|
|
SECU_PrintAlgorithmID(out, &(info->digestEncAlg),
|
|
"Digest Encryption Algorithm", level + 1);
|
|
SECU_PrintAsHex(out, &(info->encDigest), "Encrypted Digest", level + 1);
|
|
|
|
if (info->unAuthAttr != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Unauthenticated Attributes:\n");
|
|
iv = 0;
|
|
while ((attr = info->unAuthAttr[iv++]) != NULL) {
|
|
sprintf(om, "Attribute (%x)", iv);
|
|
secu_PrintAttribute(out, attr, om, level + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* callers of this function must make sure that the CERTSignedCrl
|
|
from which they are extracting the CERTCrl has been fully-decoded.
|
|
Otherwise it will not have the entries even though the CRL may have
|
|
some */
|
|
|
|
void
|
|
SECU_PrintCRLInfo(FILE *out, CERTCrl *crl, char *m, int level)
|
|
{
|
|
CERTCrlEntry *entry;
|
|
int iv;
|
|
char om[100];
|
|
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
/* version is optional */
|
|
iv = crl->version.len ? DER_GetInteger(&crl->version) : 0;
|
|
SECU_Indent(out, level+1);
|
|
fprintf(out, "%s: %d (0x%x)\n", "Version", iv + 1, iv);
|
|
SECU_PrintAlgorithmID(out, &(crl->signatureAlg), "Signature Algorithm",
|
|
level + 1);
|
|
SECU_PrintName(out, &(crl->name), "Issuer", level + 1);
|
|
SECU_PrintTimeChoice(out, &(crl->lastUpdate), "This Update", level + 1);
|
|
if (crl->nextUpdate.data && crl->nextUpdate.len) /* is optional */
|
|
SECU_PrintTimeChoice(out, &(crl->nextUpdate), "Next Update", level + 1);
|
|
|
|
if (crl->entries != NULL) {
|
|
iv = 0;
|
|
while ((entry = crl->entries[iv++]) != NULL) {
|
|
sprintf(om, "Entry %d (0x%x):\n", iv, iv);
|
|
SECU_Indent(out, level + 1); fputs(om, out);
|
|
SECU_PrintInteger(out, &(entry->serialNumber), "Serial Number",
|
|
level + 2);
|
|
SECU_PrintTimeChoice(out, &(entry->revocationDate),
|
|
"Revocation Date", level + 2);
|
|
SECU_PrintExtensions(out, entry->extensions,
|
|
"Entry Extensions", level + 2);
|
|
}
|
|
}
|
|
SECU_PrintExtensions(out, crl->extensions, "CRL Extensions", level + 1);
|
|
}
|
|
|
|
/*
|
|
** secu_PrintPKCS7Signed
|
|
** Pretty print a PKCS7 signed data type (up to version 1).
|
|
*/
|
|
static int
|
|
secu_PrintPKCS7Signed(FILE *out, SEC_PKCS7SignedData *src,
|
|
const char *m, int level)
|
|
{
|
|
SECAlgorithmID *digAlg; /* digest algorithms */
|
|
SECItem *aCert; /* certificate */
|
|
CERTSignedCrl *aCrl; /* certificate revocation list */
|
|
SEC_PKCS7SignerInfo *sigInfo; /* signer information */
|
|
int rv, iv;
|
|
char om[100];
|
|
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &(src->version), "Version", level + 1);
|
|
|
|
/* Parse and list digest algorithms (if any) */
|
|
if (src->digestAlgorithms != NULL) {
|
|
SECU_Indent(out, level + 1); fprintf(out, "Digest Algorithm List:\n");
|
|
iv = 0;
|
|
while ((digAlg = src->digestAlgorithms[iv++]) != NULL) {
|
|
sprintf(om, "Digest Algorithm (%x)", iv);
|
|
SECU_PrintAlgorithmID(out, digAlg, om, level + 2);
|
|
}
|
|
}
|
|
|
|
/* Now for the content */
|
|
rv = secu_PrintPKCS7ContentInfo(out, &(src->contentInfo),
|
|
"Content Information", level + 1);
|
|
if (rv != 0)
|
|
return rv;
|
|
|
|
/* Parse and list certificates (if any) */
|
|
if (src->rawCerts != NULL) {
|
|
SECU_Indent(out, level + 1); fprintf(out, "Certificate List:\n");
|
|
iv = 0;
|
|
while ((aCert = src->rawCerts[iv++]) != NULL) {
|
|
sprintf(om, "Certificate (%x)", iv);
|
|
rv = SECU_PrintSignedData(out, aCert, om, level + 2,
|
|
(SECU_PPFunc)SECU_PrintCertificate);
|
|
if (rv)
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
/* Parse and list CRL's (if any) */
|
|
if (src->crls != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Signed Revocation Lists:\n");
|
|
iv = 0;
|
|
while ((aCrl = src->crls[iv++]) != NULL) {
|
|
sprintf(om, "Signed Revocation List (%x)", iv);
|
|
SECU_Indent(out, level + 2); fprintf(out, "%s:\n", om);
|
|
SECU_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm,
|
|
"Signature Algorithm", level+3);
|
|
DER_ConvertBitString(&aCrl->signatureWrap.signature);
|
|
SECU_PrintAsHex(out, &aCrl->signatureWrap.signature, "Signature",
|
|
level+3);
|
|
SECU_PrintCRLInfo(out, &aCrl->crl, "Certificate Revocation List",
|
|
level + 3);
|
|
}
|
|
}
|
|
|
|
/* Parse and list signatures (if any) */
|
|
if (src->signerInfos != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Signer Information List:\n");
|
|
iv = 0;
|
|
while ((sigInfo = src->signerInfos[iv++]) != NULL) {
|
|
sprintf(om, "Signer Information (%x)", iv);
|
|
secu_PrintSignerInfo(out, sigInfo, om, level + 2);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** secu_PrintPKCS7Enveloped
|
|
** Pretty print a PKCS7 enveloped data type (up to version 1).
|
|
*/
|
|
static void
|
|
secu_PrintPKCS7Enveloped(FILE *out, SEC_PKCS7EnvelopedData *src,
|
|
const char *m, int level)
|
|
{
|
|
SEC_PKCS7RecipientInfo *recInfo; /* pointer for signer information */
|
|
int iv;
|
|
char om[100];
|
|
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &(src->version), "Version", level + 1);
|
|
|
|
/* Parse and list recipients (this is not optional) */
|
|
if (src->recipientInfos != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Recipient Information List:\n");
|
|
iv = 0;
|
|
while ((recInfo = src->recipientInfos[iv++]) != NULL) {
|
|
sprintf(om, "Recipient Information (%x)", iv);
|
|
secu_PrintRecipientInfo(out, recInfo, om, level + 2);
|
|
}
|
|
}
|
|
|
|
secu_PrintPKCS7EncContent(out, &src->encContentInfo,
|
|
"Encrypted Content Information", level + 1);
|
|
}
|
|
|
|
/*
|
|
** secu_PrintPKCS7SignedEnveloped
|
|
** Pretty print a PKCS7 singed and enveloped data type (up to version 1).
|
|
*/
|
|
static int
|
|
secu_PrintPKCS7SignedAndEnveloped(FILE *out,
|
|
SEC_PKCS7SignedAndEnvelopedData *src,
|
|
const char *m, int level)
|
|
{
|
|
SECAlgorithmID *digAlg; /* pointer for digest algorithms */
|
|
SECItem *aCert; /* pointer for certificate */
|
|
CERTSignedCrl *aCrl; /* pointer for certificate revocation list */
|
|
SEC_PKCS7SignerInfo *sigInfo; /* pointer for signer information */
|
|
SEC_PKCS7RecipientInfo *recInfo; /* pointer for recipient information */
|
|
int rv, iv;
|
|
char om[100];
|
|
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &(src->version), "Version", level + 1);
|
|
|
|
/* Parse and list recipients (this is not optional) */
|
|
if (src->recipientInfos != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Recipient Information List:\n");
|
|
iv = 0;
|
|
while ((recInfo = src->recipientInfos[iv++]) != NULL) {
|
|
sprintf(om, "Recipient Information (%x)", iv);
|
|
secu_PrintRecipientInfo(out, recInfo, om, level + 2);
|
|
}
|
|
}
|
|
|
|
/* Parse and list digest algorithms (if any) */
|
|
if (src->digestAlgorithms != NULL) {
|
|
SECU_Indent(out, level + 1); fprintf(out, "Digest Algorithm List:\n");
|
|
iv = 0;
|
|
while ((digAlg = src->digestAlgorithms[iv++]) != NULL) {
|
|
sprintf(om, "Digest Algorithm (%x)", iv);
|
|
SECU_PrintAlgorithmID(out, digAlg, om, level + 2);
|
|
}
|
|
}
|
|
|
|
secu_PrintPKCS7EncContent(out, &src->encContentInfo,
|
|
"Encrypted Content Information", level + 1);
|
|
|
|
/* Parse and list certificates (if any) */
|
|
if (src->rawCerts != NULL) {
|
|
SECU_Indent(out, level + 1); fprintf(out, "Certificate List:\n");
|
|
iv = 0;
|
|
while ((aCert = src->rawCerts[iv++]) != NULL) {
|
|
sprintf(om, "Certificate (%x)", iv);
|
|
rv = SECU_PrintSignedData(out, aCert, om, level + 2,
|
|
(SECU_PPFunc)SECU_PrintCertificate);
|
|
if (rv)
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
/* Parse and list CRL's (if any) */
|
|
if (src->crls != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Signed Revocation Lists:\n");
|
|
iv = 0;
|
|
while ((aCrl = src->crls[iv++]) != NULL) {
|
|
sprintf(om, "Signed Revocation List (%x)", iv);
|
|
SECU_Indent(out, level + 2); fprintf(out, "%s:\n", om);
|
|
SECU_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm,
|
|
"Signature Algorithm", level+3);
|
|
DER_ConvertBitString(&aCrl->signatureWrap.signature);
|
|
SECU_PrintAsHex(out, &aCrl->signatureWrap.signature, "Signature",
|
|
level+3);
|
|
SECU_PrintCRLInfo(out, &aCrl->crl, "Certificate Revocation List",
|
|
level + 3);
|
|
}
|
|
}
|
|
|
|
/* Parse and list signatures (if any) */
|
|
if (src->signerInfos != NULL) {
|
|
SECU_Indent(out, level + 1);
|
|
fprintf(out, "Signer Information List:\n");
|
|
iv = 0;
|
|
while ((sigInfo = src->signerInfos[iv++]) != NULL) {
|
|
sprintf(om, "Signer Information (%x)", iv);
|
|
secu_PrintSignerInfo(out, sigInfo, om, level + 2);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SECU_PrintCrl (FILE *out, SECItem *der, char *m, int level)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
CERTCrl *c = NULL;
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
|
|
if (!arena)
|
|
return rv;
|
|
do {
|
|
/* Decode CRL */
|
|
c = PORT_ArenaZNew(arena, CERTCrl);
|
|
if (!c)
|
|
break;
|
|
|
|
rv = SEC_QuickDERDecodeItem(arena, c, SEC_ASN1_GET(CERT_CrlTemplate), der);
|
|
if (rv != SECSuccess)
|
|
break;
|
|
SECU_PrintCRLInfo (out, c, m, level);
|
|
} while (0);
|
|
PORT_FreeArena (arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*
|
|
** secu_PrintPKCS7Encrypted
|
|
** Pretty print a PKCS7 encrypted data type (up to version 1).
|
|
*/
|
|
static void
|
|
secu_PrintPKCS7Encrypted(FILE *out, SEC_PKCS7EncryptedData *src,
|
|
const char *m, int level)
|
|
{
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &(src->version), "Version", level + 1);
|
|
|
|
secu_PrintPKCS7EncContent(out, &src->encContentInfo,
|
|
"Encrypted Content Information", level + 1);
|
|
}
|
|
|
|
/*
|
|
** secu_PrintPKCS7Digested
|
|
** Pretty print a PKCS7 digested data type (up to version 1).
|
|
*/
|
|
static void
|
|
secu_PrintPKCS7Digested(FILE *out, SEC_PKCS7DigestedData *src,
|
|
const char *m, int level)
|
|
{
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_PrintInteger(out, &(src->version), "Version", level + 1);
|
|
|
|
SECU_PrintAlgorithmID(out, &src->digestAlg, "Digest Algorithm",
|
|
level + 1);
|
|
secu_PrintPKCS7ContentInfo(out, &src->contentInfo, "Content Information",
|
|
level + 1);
|
|
SECU_PrintAsHex(out, &src->digest, "Digest", level + 1);
|
|
}
|
|
|
|
/*
|
|
** secu_PrintPKCS7ContentInfo
|
|
** Takes a SEC_PKCS7ContentInfo type and sends the contents to the
|
|
** appropriate function
|
|
*/
|
|
static int
|
|
secu_PrintPKCS7ContentInfo(FILE *out, SEC_PKCS7ContentInfo *src,
|
|
char *m, int level)
|
|
{
|
|
const char *desc;
|
|
SECOidTag kind;
|
|
int rv;
|
|
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
level++;
|
|
|
|
if (src->contentTypeTag == NULL)
|
|
src->contentTypeTag = SECOID_FindOID(&(src->contentType));
|
|
|
|
if (src->contentTypeTag == NULL) {
|
|
desc = "Unknown";
|
|
kind = SEC_OID_PKCS7_DATA;
|
|
} else {
|
|
desc = src->contentTypeTag->desc;
|
|
kind = src->contentTypeTag->offset;
|
|
}
|
|
|
|
if (src->content.data == NULL) {
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", desc);
|
|
level++;
|
|
SECU_Indent(out, level); fprintf(out, "<no content>\n");
|
|
return 0;
|
|
}
|
|
|
|
rv = 0;
|
|
switch (kind) {
|
|
case SEC_OID_PKCS7_SIGNED_DATA: /* Signed Data */
|
|
rv = secu_PrintPKCS7Signed(out, src->content.signedData, desc, level);
|
|
break;
|
|
|
|
case SEC_OID_PKCS7_ENVELOPED_DATA: /* Enveloped Data */
|
|
secu_PrintPKCS7Enveloped(out, src->content.envelopedData, desc, level);
|
|
break;
|
|
|
|
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: /* Signed and Enveloped */
|
|
rv = secu_PrintPKCS7SignedAndEnveloped(out,
|
|
src->content.signedAndEnvelopedData,
|
|
desc, level);
|
|
break;
|
|
|
|
case SEC_OID_PKCS7_DIGESTED_DATA: /* Digested Data */
|
|
secu_PrintPKCS7Digested(out, src->content.digestedData, desc, level);
|
|
break;
|
|
|
|
case SEC_OID_PKCS7_ENCRYPTED_DATA: /* Encrypted Data */
|
|
secu_PrintPKCS7Encrypted(out, src->content.encryptedData, desc, level);
|
|
break;
|
|
|
|
default:
|
|
SECU_PrintAsHex(out, src->content.data, desc, level);
|
|
break;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
** SECU_PrintPKCS7ContentInfo
|
|
** Decode and print any major PKCS7 data type (up to version 1).
|
|
*/
|
|
int
|
|
SECU_PrintPKCS7ContentInfo(FILE *out, SECItem *der, char *m, int level)
|
|
{
|
|
SEC_PKCS7ContentInfo *cinfo;
|
|
int rv;
|
|
|
|
cinfo = SEC_PKCS7DecodeItem(der, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
if (cinfo != NULL) {
|
|
/* Send it to recursive parsing and printing module */
|
|
rv = secu_PrintPKCS7ContentInfo(out, cinfo, m, level);
|
|
SEC_PKCS7DestroyContentInfo(cinfo);
|
|
} else {
|
|
rv = -1;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
** End of PKCS7 functions
|
|
*/
|
|
|
|
void
|
|
printFlags(FILE *out, unsigned int flags, int level)
|
|
{
|
|
if ( flags & CERTDB_TERMINAL_RECORD ) {
|
|
SECU_Indent(out, level); fprintf(out, "Terminal Record\n");
|
|
}
|
|
if ( flags & CERTDB_TRUSTED ) {
|
|
SECU_Indent(out, level); fprintf(out, "Trusted\n");
|
|
}
|
|
if ( flags & CERTDB_SEND_WARN ) {
|
|
SECU_Indent(out, level); fprintf(out, "Warn When Sending\n");
|
|
}
|
|
if ( flags & CERTDB_VALID_CA ) {
|
|
SECU_Indent(out, level); fprintf(out, "Valid CA\n");
|
|
}
|
|
if ( flags & CERTDB_TRUSTED_CA ) {
|
|
SECU_Indent(out, level); fprintf(out, "Trusted CA\n");
|
|
}
|
|
if ( flags & CERTDB_NS_TRUSTED_CA ) {
|
|
SECU_Indent(out, level); fprintf(out, "Netscape Trusted CA\n");
|
|
}
|
|
if ( flags & CERTDB_USER ) {
|
|
SECU_Indent(out, level); fprintf(out, "User\n");
|
|
}
|
|
if ( flags & CERTDB_TRUSTED_CLIENT_CA ) {
|
|
SECU_Indent(out, level); fprintf(out, "Trusted Client CA\n");
|
|
}
|
|
if ( flags & CERTDB_GOVT_APPROVED_CA ) {
|
|
SECU_Indent(out, level); fprintf(out, "Step-up\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
SECU_PrintTrustFlags(FILE *out, CERTCertTrust *trust, char *m, int level)
|
|
{
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
SECU_Indent(out, level+1); fprintf(out, "SSL Flags:\n");
|
|
printFlags(out, trust->sslFlags, level+2);
|
|
SECU_Indent(out, level+1); fprintf(out, "Email Flags:\n");
|
|
printFlags(out, trust->emailFlags, level+2);
|
|
SECU_Indent(out, level+1); fprintf(out, "Object Signing Flags:\n");
|
|
printFlags(out, trust->objectSigningFlags, level+2);
|
|
}
|
|
|
|
int SECU_PrintDERName(FILE *out, SECItem *der, const char *m, int level)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
CERTName *name;
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
|
|
if (!arena)
|
|
return rv;
|
|
|
|
name = PORT_ArenaZNew(arena, CERTName);
|
|
if (!name)
|
|
goto loser;
|
|
|
|
rv = SEC_ASN1DecodeItem(arena, name, SEC_ASN1_GET(CERT_NameTemplate), der);
|
|
if (rv)
|
|
goto loser;
|
|
|
|
SECU_PrintName(out, name, m, level);
|
|
if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/
|
|
SECU_Newline(out);
|
|
loser:
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
typedef enum {
|
|
noSignature = 0,
|
|
withSignature = 1
|
|
} SignatureOptionType;
|
|
|
|
static int
|
|
secu_PrintSignedDataSigOpt(FILE *out, SECItem *der, const char *m,
|
|
int level, SECU_PPFunc inner,
|
|
SignatureOptionType withSignature)
|
|
{
|
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
CERTSignedData *sd;
|
|
int rv = SEC_ERROR_NO_MEMORY;
|
|
|
|
if (!arena)
|
|
return rv;
|
|
|
|
/* Strip off the signature */
|
|
sd = PORT_ArenaZNew(arena, CERTSignedData);
|
|
if (!sd)
|
|
goto loser;
|
|
|
|
rv = SEC_ASN1DecodeItem(arena, sd, SEC_ASN1_GET(CERT_SignedDataTemplate),
|
|
der);
|
|
if (rv)
|
|
goto loser;
|
|
|
|
if (m) {
|
|
SECU_Indent(out, level); fprintf(out, "%s:\n", m);
|
|
} else {
|
|
level -= 1;
|
|
}
|
|
rv = (*inner)(out, &sd->data, "Data", level+1);
|
|
|
|
if (withSignature) {
|
|
SECU_PrintAlgorithmID(out, &sd->signatureAlgorithm, "Signature Algorithm",
|
|
level+1);
|
|
DER_ConvertBitString(&sd->signature);
|
|
SECU_PrintAsHex(out, &sd->signature, "Signature", level+1);
|
|
}
|
|
SECU_PrintFingerprints(out, der, "Fingerprint", level+1);
|
|
loser:
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
int SECU_PrintSignedData(FILE *out, SECItem *der, const char *m,
|
|
int level, SECU_PPFunc inner)
|
|
{
|
|
return secu_PrintSignedDataSigOpt(out, der, m, level, inner,
|
|
withSignature);
|
|
}
|
|
|
|
int SECU_PrintSignedContent(FILE *out, SECItem *der, char *m,
|
|
int level, SECU_PPFunc inner)
|
|
{
|
|
return secu_PrintSignedDataSigOpt(out, der, m, level, inner,
|
|
noSignature);
|
|
}
|
|
|
|
SECStatus
|
|
SEC_PrintCertificateAndTrust(CERTCertificate *cert,
|
|
const char *label,
|
|
CERTCertTrust *trust)
|
|
{
|
|
SECStatus rv;
|
|
SECItem data;
|
|
CERTCertTrust certTrust;
|
|
|
|
data.data = cert->derCert.data;
|
|
data.len = cert->derCert.len;
|
|
|
|
rv = SECU_PrintSignedData(stdout, &data, label, 0,
|
|
(SECU_PPFunc)SECU_PrintCertificate);
|
|
if (rv) {
|
|
return(SECFailure);
|
|
}
|
|
if (trust) {
|
|
SECU_PrintTrustFlags(stdout, trust,
|
|
"Certificate Trust Flags", 1);
|
|
} else if (CERT_GetCertTrust(cert, &certTrust) == SECSuccess) {
|
|
SECU_PrintTrustFlags(stdout, &certTrust,
|
|
"Certificate Trust Flags", 1);
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
return(SECSuccess);
|
|
}
|
|
|
|
|
|
static char *
|
|
bestCertName(CERTCertificate *cert) {
|
|
if (cert->nickname) {
|
|
return cert->nickname;
|
|
}
|
|
if (cert->emailAddr && cert->emailAddr[0]) {
|
|
return cert->emailAddr;
|
|
}
|
|
return cert->subjectName;
|
|
}
|
|
|
|
void
|
|
SECU_printCertProblemsOnDate(FILE *outfile, CERTCertDBHandle *handle,
|
|
CERTCertificate *cert, PRBool checksig,
|
|
SECCertificateUsage certUsage, void *pinArg, PRBool verbose,
|
|
PRTime datetime)
|
|
{
|
|
CERTVerifyLog log;
|
|
CERTVerifyLogNode *node;
|
|
|
|
PRErrorCode err = PORT_GetError();
|
|
|
|
log.arena = PORT_NewArena(512);
|
|
log.head = log.tail = NULL;
|
|
log.count = 0;
|
|
CERT_VerifyCertificate(handle, cert, checksig, certUsage, datetime, pinArg, &log, NULL);
|
|
|
|
SECU_displayVerifyLog(outfile, &log, verbose);
|
|
|
|
for (node = log.head; node; node = node->next) {
|
|
if (node->cert)
|
|
CERT_DestroyCertificate(node->cert);
|
|
}
|
|
PORT_FreeArena(log.arena, PR_FALSE);
|
|
|
|
PORT_SetError(err); /* restore original error code */
|
|
}
|
|
|
|
void
|
|
SECU_displayVerifyLog(FILE *outfile, CERTVerifyLog *log,
|
|
PRBool verbose)
|
|
{
|
|
CERTVerifyLogNode *node = NULL;
|
|
unsigned int depth = (unsigned int)-1;
|
|
unsigned int flags = 0;
|
|
char * errstr = NULL;
|
|
|
|
if (log->count > 0) {
|
|
fprintf(outfile,"PROBLEM WITH THE CERT CHAIN:\n");
|
|
for (node = log->head; node; node = node->next) {
|
|
if (depth != node->depth) {
|
|
depth = node->depth;
|
|
fprintf(outfile,"CERT %d. %s %s:\n", depth,
|
|
bestCertName(node->cert),
|
|
depth ? "[Certificate Authority]": "");
|
|
if (verbose) {
|
|
const char * emailAddr;
|
|
emailAddr = CERT_GetFirstEmailAddress(node->cert);
|
|
if (emailAddr) {
|
|
fprintf(outfile,"Email Address(es): ");
|
|
do {
|
|
fprintf(outfile, "%s\n", emailAddr);
|
|
emailAddr = CERT_GetNextEmailAddress(node->cert,
|
|
emailAddr);
|
|
} while (emailAddr);
|
|
}
|
|
}
|
|
}
|
|
fprintf(outfile, " ERROR %ld: %s\n", node->error,
|
|
SECU_Strerror(node->error));
|
|
errstr = NULL;
|
|
switch (node->error) {
|
|
case SEC_ERROR_INADEQUATE_KEY_USAGE:
|
|
flags = (unsigned int)((char *)node->arg - (char *)NULL);
|
|
switch (flags) {
|
|
case KU_DIGITAL_SIGNATURE:
|
|
errstr = "Cert cannot sign.";
|
|
break;
|
|
case KU_KEY_ENCIPHERMENT:
|
|
errstr = "Cert cannot encrypt.";
|
|
break;
|
|
case KU_KEY_CERT_SIGN:
|
|
errstr = "Cert cannot sign other certs.";
|
|
break;
|
|
default:
|
|
errstr = "[unknown usage].";
|
|
break;
|
|
}
|
|
case SEC_ERROR_INADEQUATE_CERT_TYPE:
|
|
flags = (unsigned int)((char *)node->arg - (char *)NULL);
|
|
switch (flags) {
|
|
case NS_CERT_TYPE_SSL_CLIENT:
|
|
case NS_CERT_TYPE_SSL_SERVER:
|
|
errstr = "Cert cannot be used for SSL.";
|
|
break;
|
|
case NS_CERT_TYPE_SSL_CA:
|
|
errstr = "Cert cannot be used as an SSL CA.";
|
|
break;
|
|
case NS_CERT_TYPE_EMAIL:
|
|
errstr = "Cert cannot be used for SMIME.";
|
|
break;
|
|
case NS_CERT_TYPE_EMAIL_CA:
|
|
errstr = "Cert cannot be used as an SMIME CA.";
|
|
break;
|
|
case NS_CERT_TYPE_OBJECT_SIGNING:
|
|
errstr = "Cert cannot be used for object signing.";
|
|
break;
|
|
case NS_CERT_TYPE_OBJECT_SIGNING_CA:
|
|
errstr = "Cert cannot be used as an object signing CA.";
|
|
break;
|
|
default:
|
|
errstr = "[unknown usage].";
|
|
break;
|
|
}
|
|
case SEC_ERROR_UNKNOWN_ISSUER:
|
|
case SEC_ERROR_UNTRUSTED_ISSUER:
|
|
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
|
|
errstr = node->cert->issuerName;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (errstr) {
|
|
fprintf(stderr," %s\n",errstr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
SECU_printCertProblems(FILE *outfile, CERTCertDBHandle *handle,
|
|
CERTCertificate *cert, PRBool checksig,
|
|
SECCertificateUsage certUsage, void *pinArg, PRBool verbose)
|
|
{
|
|
SECU_printCertProblemsOnDate(outfile, handle, cert, checksig,
|
|
certUsage, pinArg, verbose, PR_Now());
|
|
}
|
|
|
|
SECStatus
|
|
SECU_StoreCRL(PK11SlotInfo *slot, SECItem *derCrl, PRFileDesc *outFile,
|
|
PRBool ascii, char *url)
|
|
{
|
|
PORT_Assert(derCrl != NULL);
|
|
if (!derCrl) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (outFile != NULL) {
|
|
if (ascii) {
|
|
PR_fprintf(outFile, "%s\n%s\n%s\n", NS_CRL_HEADER,
|
|
BTOA_DataToAscii(derCrl->data, derCrl->len),
|
|
NS_CRL_TRAILER);
|
|
} else {
|
|
if (PR_Write(outFile, derCrl->data, derCrl->len) != derCrl->len) {
|
|
return SECFailure;
|
|
}
|
|
}
|
|
}
|
|
if (slot) {
|
|
CERTSignedCrl *newCrl = PK11_ImportCRL(slot, derCrl, url,
|
|
SEC_CRL_TYPE, NULL, 0, NULL, 0);
|
|
if (newCrl != NULL) {
|
|
SEC_DestroyCrl(newCrl);
|
|
return SECSuccess;
|
|
}
|
|
return SECFailure;
|
|
}
|
|
if (!outFile && !slot) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
SECU_SignAndEncodeCRL(CERTCertificate *issuer, CERTSignedCrl *signCrl,
|
|
SECOidTag hashAlgTag, SignAndEncodeFuncExitStat *resCode)
|
|
{
|
|
SECItem der;
|
|
SECKEYPrivateKey *caPrivateKey = NULL;
|
|
SECStatus rv;
|
|
PLArenaPool *arena;
|
|
SECOidTag algID;
|
|
void *dummy;
|
|
|
|
PORT_Assert(issuer != NULL && signCrl != NULL);
|
|
if (!issuer || !signCrl) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
arena = signCrl->arena;
|
|
|
|
caPrivateKey = PK11_FindKeyByAnyCert(issuer, NULL);
|
|
if (caPrivateKey == NULL) {
|
|
*resCode = noKeyFound;
|
|
return SECFailure;
|
|
}
|
|
|
|
algID = SEC_GetSignatureAlgorithmOidTag(caPrivateKey->keyType, hashAlgTag);
|
|
if (algID == SEC_OID_UNKNOWN) {
|
|
*resCode = noSignatureMatch;
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
if (!signCrl->crl.signatureAlg.parameters.data) {
|
|
rv = SECOID_SetAlgorithmID(arena, &signCrl->crl.signatureAlg, algID, 0);
|
|
if (rv != SECSuccess) {
|
|
*resCode = failToEncode;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
der.len = 0;
|
|
der.data = NULL;
|
|
dummy = SEC_ASN1EncodeItem(arena, &der, &signCrl->crl,
|
|
SEC_ASN1_GET(CERT_CrlTemplate));
|
|
if (!dummy) {
|
|
*resCode = failToEncode;
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
rv = SECU_DerSignDataCRL(arena, &signCrl->signatureWrap,
|
|
der.data, der.len, caPrivateKey, algID);
|
|
if (rv != SECSuccess) {
|
|
*resCode = failToSign;
|
|
goto done;
|
|
}
|
|
|
|
signCrl->derCrl = PORT_ArenaZNew(arena, SECItem);
|
|
if (signCrl->derCrl == NULL) {
|
|
*resCode = noMem;
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
signCrl->derCrl->len = 0;
|
|
signCrl->derCrl->data = NULL;
|
|
dummy = SEC_ASN1EncodeItem (arena, signCrl->derCrl, signCrl,
|
|
SEC_ASN1_GET(CERT_SignedCrlTemplate));
|
|
if (!dummy) {
|
|
*resCode = failToEncode;
|
|
rv = SECFailure;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if (caPrivateKey) {
|
|
SECKEY_DestroyPrivateKey(caPrivateKey);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
SECU_CopyCRL(PLArenaPool *destArena, CERTCrl *destCrl, CERTCrl *srcCrl)
|
|
{
|
|
void *dummy;
|
|
SECStatus rv = SECSuccess;
|
|
SECItem der;
|
|
|
|
PORT_Assert(destArena && srcCrl && destCrl);
|
|
if (!destArena || !srcCrl || !destCrl) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
der.len = 0;
|
|
der.data = NULL;
|
|
dummy = SEC_ASN1EncodeItem (destArena, &der, srcCrl,
|
|
SEC_ASN1_GET(CERT_CrlTemplate));
|
|
if (!dummy) {
|
|
return SECFailure;
|
|
}
|
|
|
|
rv = SEC_QuickDERDecodeItem(destArena, destCrl,
|
|
SEC_ASN1_GET(CERT_CrlTemplate), &der);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
|
|
destCrl->arena = destArena;
|
|
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SECU_DerSignDataCRL(PLArenaPool *arena, CERTSignedData *sd,
|
|
unsigned char *buf, int len, SECKEYPrivateKey *pk,
|
|
SECOidTag algID)
|
|
{
|
|
SECItem it;
|
|
SECStatus rv;
|
|
|
|
it.data = 0;
|
|
|
|
/* XXX We should probably have some asserts here to make sure the key type
|
|
* and algID match
|
|
*/
|
|
|
|
/* Sign input buffer */
|
|
rv = SEC_SignData(&it, buf, len, pk, algID);
|
|
if (rv) goto loser;
|
|
|
|
/* Fill out SignedData object */
|
|
PORT_Memset(sd, 0, sizeof(*sd));
|
|
sd->data.data = buf;
|
|
sd->data.len = len;
|
|
sd->signature.data = it.data;
|
|
sd->signature.len = it.len << 3; /* convert to bit string */
|
|
rv = SECOID_SetAlgorithmID(arena, &sd->signatureAlgorithm, algID, 0);
|
|
if (rv) goto loser;
|
|
|
|
return rv;
|
|
|
|
loser:
|
|
PORT_Free(it.data);
|
|
return rv;
|
|
}
|
|
|
|
#if 0
|
|
|
|
/* we need access to the private function cert_FindExtension for this code to work */
|
|
|
|
CERTAuthKeyID *
|
|
SECU_FindCRLAuthKeyIDExten (PLArenaPool *arena, CERTSignedCrl *scrl)
|
|
{
|
|
SECItem encodedExtenValue;
|
|
SECStatus rv;
|
|
CERTAuthKeyID *ret;
|
|
CERTCrl* crl;
|
|
|
|
if (!scrl) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return NULL;
|
|
}
|
|
|
|
crl = &scrl->crl;
|
|
|
|
encodedExtenValue.data = NULL;
|
|
encodedExtenValue.len = 0;
|
|
|
|
rv = cert_FindExtension(crl->extensions, SEC_OID_X509_AUTH_KEY_ID,
|
|
&encodedExtenValue);
|
|
if ( rv != SECSuccess ) {
|
|
return (NULL);
|
|
}
|
|
|
|
ret = CERT_DecodeAuthKeyID (arena, &encodedExtenValue);
|
|
|
|
PORT_Free(encodedExtenValue.data);
|
|
encodedExtenValue.data = NULL;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Find the issuer of a Crl. Use the authorityKeyID if it exists.
|
|
*/
|
|
CERTCertificate *
|
|
SECU_FindCrlIssuer(CERTCertDBHandle *dbhandle, SECItem* subject,
|
|
CERTAuthKeyID* authorityKeyID, PRTime validTime)
|
|
{
|
|
CERTCertificate *issuerCert = NULL;
|
|
CERTCertList *certList = NULL;
|
|
CERTCertTrust trust;
|
|
|
|
if (!subject) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return NULL;
|
|
}
|
|
|
|
certList =
|
|
CERT_CreateSubjectCertList(NULL, dbhandle, subject,
|
|
validTime, PR_TRUE);
|
|
if (certList) {
|
|
CERTCertListNode *node = CERT_LIST_HEAD(certList);
|
|
|
|
/* XXX and authoritykeyid in the future */
|
|
while ( ! CERT_LIST_END(node, certList) ) {
|
|
CERTCertificate *cert = node->cert;
|
|
/* check cert CERTCertTrust data is allocated, check cert
|
|
usage extension, check that cert has pkey in db. Select
|
|
the first (newest) user cert */
|
|
if (CERT_GetCertTrust(cert, &trust) == SECSuccess &&
|
|
CERT_CheckCertUsage(cert, KU_CRL_SIGN) == SECSuccess &&
|
|
CERT_IsUserCert(cert)) {
|
|
|
|
issuerCert = CERT_DupCertificate(cert);
|
|
break;
|
|
}
|
|
node = CERT_LIST_NEXT(node);
|
|
}
|
|
CERT_DestroyCertList(certList);
|
|
}
|
|
return(issuerCert);
|
|
}
|
|
|
|
|
|
/* Encodes and adds extensions to the CRL or CRL entries. */
|
|
SECStatus
|
|
SECU_EncodeAndAddExtensionValue(PLArenaPool *arena, void *extHandle,
|
|
void *value, PRBool criticality, int extenType,
|
|
EXTEN_EXT_VALUE_ENCODER EncodeValueFn)
|
|
{
|
|
SECItem encodedValue;
|
|
SECStatus rv;
|
|
|
|
encodedValue.data = NULL;
|
|
encodedValue.len = 0;
|
|
do {
|
|
rv = (*EncodeValueFn)(arena, value, &encodedValue);
|
|
if (rv != SECSuccess)
|
|
break;
|
|
|
|
rv = CERT_AddExtension(extHandle, extenType, &encodedValue,
|
|
criticality, PR_TRUE);
|
|
if (rv != SECSuccess)
|
|
break;
|
|
} while (0);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
CERTCertificate*
|
|
SECU_FindCertByNicknameOrFilename(CERTCertDBHandle *handle,
|
|
char *name, PRBool ascii,
|
|
void *pwarg)
|
|
{
|
|
CERTCertificate *the_cert;
|
|
the_cert = CERT_FindCertByNicknameOrEmailAddr(handle, name);
|
|
if (the_cert) {
|
|
return the_cert;
|
|
}
|
|
the_cert = PK11_FindCertFromNickname(name, pwarg);
|
|
if (!the_cert) {
|
|
/* Don't have a cert with name "name" in the DB. Try to
|
|
* open a file with such name and get the cert from there.*/
|
|
SECStatus rv;
|
|
SECItem item = {0, NULL, 0};
|
|
PRFileDesc* fd = PR_Open(name, PR_RDONLY, 0777);
|
|
if (!fd) {
|
|
return NULL;
|
|
}
|
|
rv = SECU_ReadDERFromFile(&item, fd, ascii, PR_FALSE);
|
|
PR_Close(fd);
|
|
if (rv != SECSuccess || !item.len) {
|
|
PORT_Free(item.data);
|
|
return NULL;
|
|
}
|
|
the_cert = CERT_NewTempCertificate(handle, &item,
|
|
NULL /* nickname */,
|
|
PR_FALSE /* isPerm */,
|
|
PR_TRUE /* copyDER */);
|
|
PORT_Free(item.data);
|
|
}
|
|
return the_cert;
|
|
}
|
|
|
|
/* Convert a SSL/TLS protocol version string into the respective numeric value
|
|
* defined by the SSL_LIBRARY_VERSION_* constants,
|
|
* while accepting a flexible set of case-insensitive identifiers.
|
|
*
|
|
* Caller must specify bufLen, allowing the function to operate on substrings.
|
|
*/
|
|
static SECStatus
|
|
SECU_GetSSLVersionFromName(const char *buf, size_t bufLen, PRUint16 *version)
|
|
{
|
|
if (!buf || !version) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (!PL_strncasecmp(buf, "ssl2", bufLen)) {
|
|
*version = SSL_LIBRARY_VERSION_2;
|
|
return SECSuccess;
|
|
}
|
|
if (!PL_strncasecmp(buf, "ssl3", bufLen)) {
|
|
*version = SSL_LIBRARY_VERSION_3_0;
|
|
return SECSuccess;
|
|
}
|
|
if (!PL_strncasecmp(buf, "tls1.0", bufLen)) {
|
|
*version = SSL_LIBRARY_VERSION_TLS_1_0;
|
|
return SECSuccess;
|
|
}
|
|
if (!PL_strncasecmp(buf, "tls1.1", bufLen)) {
|
|
*version = SSL_LIBRARY_VERSION_TLS_1_1;
|
|
return SECSuccess;
|
|
}
|
|
if (!PL_strncasecmp(buf, "tls1.2", bufLen)) {
|
|
*version = SSL_LIBRARY_VERSION_TLS_1_2;
|
|
return SECSuccess;
|
|
}
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus
|
|
SECU_ParseSSLVersionRangeString(const char *input,
|
|
const SSLVersionRange defaultVersionRange,
|
|
const PRBool defaultEnableSSL2,
|
|
SSLVersionRange *vrange, PRBool *enableSSL2)
|
|
{
|
|
const char *colonPos;
|
|
size_t colonIndex;
|
|
const char *maxStr;
|
|
|
|
if (!input || !vrange || !enableSSL2) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (!strcmp(input, ":")) {
|
|
/* special value, use default */
|
|
*enableSSL2 = defaultEnableSSL2;
|
|
*vrange = defaultVersionRange;
|
|
return SECSuccess;
|
|
}
|
|
|
|
colonPos = strchr(input, ':');
|
|
if (!colonPos) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
colonIndex = colonPos - input;
|
|
maxStr = colonPos + 1;
|
|
|
|
if (!colonIndex) {
|
|
/* colon was first character, min version is empty */
|
|
*enableSSL2 = defaultEnableSSL2;
|
|
vrange->min = defaultVersionRange.min;
|
|
} else {
|
|
PRUint16 version;
|
|
/* colonIndex is equivalent to the length of the min version substring */
|
|
if (SECU_GetSSLVersionFromName(input, colonIndex, &version) != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (version == SSL_LIBRARY_VERSION_2) {
|
|
*enableSSL2 = PR_TRUE;
|
|
vrange->min = defaultVersionRange.min;
|
|
} else {
|
|
*enableSSL2 = PR_FALSE;
|
|
vrange->min = version;
|
|
}
|
|
}
|
|
|
|
if (!*maxStr) {
|
|
vrange->max = defaultVersionRange.max;
|
|
} else {
|
|
PRUint16 version;
|
|
/* if max version is empty, then maxStr points to the string terminator */
|
|
if (SECU_GetSSLVersionFromName(maxStr, strlen(maxStr), &version)
|
|
!= SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (version == SSL_LIBRARY_VERSION_2) {
|
|
/* consistency checking, require that min allows enableSSL2, too */
|
|
if (!*enableSSL2) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
/* we use 0 because SSL_LIBRARY_VERSION_NONE is private: */
|
|
vrange->min = 0;
|
|
vrange->max = 0;
|
|
} else {
|
|
vrange->max = version;
|
|
}
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|