/* 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 #include #include #ifdef XP_UNIX #include #endif /* for SEC_TraverseNames */ #include "cert.h" #include "certt.h" #include "certdb.h" #include "secmod.h" #include "pk11func.h" #include "secoid.h" static char consoleName[] = { #ifdef XP_UNIX "/dev/tty" #else #ifdef XP_OS2 "\\DEV\\CON" #else "CON:" #endif #endif }; #include "nssutil.h" #include "ssl.h" #include "sslproto.h" static PRBool utf8DisplayEnabled = PR_FALSE; /* The minimum password/pin length (in Unicode characters) in FIPS mode, * defined in lib/softoken/pkcs11i.h. */ #define FIPS_MIN_PIN 7 void SECU_EnableUtf8Display(PRBool enable) { utf8DisplayEnabled = enable; } PRBool SECU_GetUtf8DisplayEnabled(void) { return utf8DisplayEnabled; } static void secu_ClearPassword(char *p) { if (p) { PORT_Memset(p, 0, PORT_Strlen(p)); PORT_Free(p); } } char * SECU_GetPasswordString(void *arg, char *prompt) { #ifndef _WINDOWS char *p = NULL; FILE *input, *output; /* open terminal */ input = fopen(consoleName, "r"); if (input == NULL) { fprintf(stderr, "Error opening input terminal for read\n"); return NULL; } output = fopen(consoleName, "w"); if (output == NULL) { fprintf(stderr, "Error opening output terminal for write\n"); fclose(input); return NULL; } p = SEC_GetPassword(input, output, prompt, SEC_BlindCheckPassword); fclose(input); fclose(output); return p; #else /* Win32 version of above. opening the console may fail on windows95, and certainly isn't necessary.. */ char *p = NULL; p = SEC_GetPassword(stdin, stdout, prompt, SEC_BlindCheckPassword); return p; #endif } /* * p a s s w o r d _ h a r d c o d e * * A function to use the password passed in the -f(pwfile) argument * of the command line. * After use once, null it out otherwise PKCS11 calls us forever.? * */ char * SECU_FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg) { char *phrases, *phrase; PRFileDesc *fd; PRInt32 nb; char *pwFile = arg; int i; const long maxPwdFileSize = 4096; char *tokenName = NULL; int tokenLen = 0; if (!pwFile) return 0; if (retry) { return 0; /* no good retrying - the files contents will be the same */ } phrases = PORT_ZAlloc(maxPwdFileSize); if (!phrases) { return 0; /* out of memory */ } fd = PR_Open(pwFile, PR_RDONLY, 0); if (!fd) { fprintf(stderr, "No password file \"%s\" exists.\n", pwFile); PORT_Free(phrases); return NULL; } nb = PR_Read(fd, phrases, maxPwdFileSize); PR_Close(fd); if (nb == 0) { fprintf(stderr, "password file contains no data\n"); PORT_Free(phrases); return NULL; } if (slot) { tokenName = PK11_GetTokenName(slot); if (tokenName) { tokenLen = PORT_Strlen(tokenName); } } i = 0; do { int startphrase = i; int phraseLen; /* handle the Windows EOL case */ while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) i++; /* terminate passphrase */ phrases[i++] = '\0'; /* clean up any EOL before the start of the next passphrase */ while ((i < nb) && (phrases[i] == '\r' || phrases[i] == '\n')) { phrases[i++] = '\0'; } /* now analyze the current passphrase */ phrase = &phrases[startphrase]; if (!tokenName) break; if (PORT_Strncmp(phrase, tokenName, tokenLen)) continue; phraseLen = PORT_Strlen(phrase); if (phraseLen < (tokenLen + 1)) continue; if (phrase[tokenLen] != ':') continue; phrase = &phrase[tokenLen + 1]; break; } while (i < nb); phrase = PORT_Strdup((char *)phrase); PORT_Free(phrases); return phrase; } char * SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg) { char prompt[255]; secuPWData *pwdata = (secuPWData *)arg; secuPWData pwnull = { PW_NONE, 0 }; secuPWData pwxtrn = { PW_EXTERNAL, "external" }; if (pwdata == NULL) pwdata = &pwnull; if (PK11_ProtectedAuthenticationPath(slot)) { pwdata = &pwxtrn; } if (retry && pwdata->source != PW_NONE) { PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n"); return NULL; } switch (pwdata->source) { case PW_NONE: sprintf(prompt, "Enter Password or Pin for \"%s\":", PK11_GetTokenName(slot)); return SECU_GetPasswordString(NULL, prompt); case PW_FROMFILE: return SECU_FilePasswd(slot, retry, pwdata->data); case PW_EXTERNAL: sprintf(prompt, "Press Enter, then enter PIN for \"%s\" on external device.\n", PK11_GetTokenName(slot)); char *pw = SECU_GetPasswordString(NULL, prompt); PORT_Free(pw); /* Fall Through */ case PW_PLAINTEXT: return PL_strdup(pwdata->data); default: break; } PR_fprintf(PR_STDERR, "Password check failed: No password found.\n"); return NULL; } char * secu_InitSlotPassword(PK11SlotInfo *slot, PRBool retry, void *arg) { char *p0 = NULL; char *p1 = NULL; FILE *input, *output; secuPWData *pwdata = arg; if (pwdata->source == PW_FROMFILE) { return SECU_FilePasswd(slot, retry, pwdata->data); } if (pwdata->source == PW_PLAINTEXT) { return PL_strdup(pwdata->data); } /* PW_NONE - get it from tty */ /* open terminal */ #ifdef _WINDOWS input = stdin; #else input = fopen(consoleName, "r"); #endif if (input == NULL) { PR_fprintf(PR_STDERR, "Error opening input terminal for read\n"); return NULL; } /* we have no password, so initialize database with one */ if (PK11_IsFIPS()) { PR_fprintf(PR_STDERR, "Enter a password which will be used to encrypt your keys.\n" "The password should be at least %d characters long,\n" "and should consist of at least three character classes.\n" "The available character classes are: digits (0-9), ASCII\n" "lowercase letters, ASCII uppercase letters, ASCII\n" "non-alphanumeric characters, and non-ASCII characters.\n\n" "If an ASCII uppercase letter appears at the beginning of\n" "the password, it is not counted toward its character class.\n" "Similarly, if a digit appears at the end of the password,\n" "it is not counted toward its character class.\n\n", FIPS_MIN_PIN); } else { PR_fprintf(PR_STDERR, "Enter a password which will be used to encrypt your keys.\n" "The password should be at least 8 characters long,\n" "and should contain at least one non-alphabetic character.\n\n"); } output = fopen(consoleName, "w"); if (output == NULL) { PR_fprintf(PR_STDERR, "Error opening output terminal for write\n"); #ifndef _WINDOWS fclose(input); #endif return NULL; } for (;;) { if (p0) PORT_Free(p0); p0 = SEC_GetPassword(input, output, "Enter new password: ", SEC_BlindCheckPassword); if (p1) PORT_Free(p1); p1 = SEC_GetPassword(input, output, "Re-enter password: ", SEC_BlindCheckPassword); if (p0 && p1 && !PORT_Strcmp(p0, p1)) { break; } PR_fprintf(PR_STDERR, "Passwords do not match. Try again.\n"); } /* clear out the duplicate password string */ secu_ClearPassword(p1); fclose(input); fclose(output); return p0; } SECStatus SECU_ChangePW(PK11SlotInfo *slot, char *passwd, char *pwFile) { return SECU_ChangePW2(slot, passwd, 0, pwFile, 0); } SECStatus SECU_ChangePW2(PK11SlotInfo *slot, char *oldPass, char *newPass, char *oldPwFile, char *newPwFile) { SECStatus rv; secuPWData pwdata, newpwdata; char *oldpw = NULL, *newpw = NULL; if (oldPass) { pwdata.source = PW_PLAINTEXT; pwdata.data = oldPass; } else if (oldPwFile) { pwdata.source = PW_FROMFILE; pwdata.data = oldPwFile; } else { pwdata.source = PW_NONE; pwdata.data = NULL; } if (newPass) { newpwdata.source = PW_PLAINTEXT; newpwdata.data = newPass; } else if (newPwFile) { newpwdata.source = PW_FROMFILE; newpwdata.data = newPwFile; } else { newpwdata.source = PW_NONE; newpwdata.data = NULL; } if (PK11_NeedUserInit(slot)) { newpw = secu_InitSlotPassword(slot, PR_FALSE, &pwdata); rv = PK11_InitPin(slot, (char *)NULL, newpw); goto done; } for (;;) { oldpw = SECU_GetModulePassword(slot, PR_FALSE, &pwdata); if (PK11_CheckUserPassword(slot, oldpw) != SECSuccess) { if (pwdata.source == PW_NONE) { PR_fprintf(PR_STDERR, "Invalid password. Try again.\n"); } else { PR_fprintf(PR_STDERR, "Invalid password.\n"); PORT_Memset(oldpw, 0, PL_strlen(oldpw)); PORT_Free(oldpw); rv = SECFailure; goto done; } } else break; PORT_Free(oldpw); } newpw = secu_InitSlotPassword(slot, PR_FALSE, &newpwdata); rv = PK11_ChangePW(slot, oldpw, newpw); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed to change password.\n"); } else { PR_fprintf(PR_STDOUT, "Password changed successfully.\n"); } PORT_Memset(oldpw, 0, PL_strlen(oldpw)); PORT_Free(oldpw); done: if (newpw) { PORT_Memset(newpw, 0, PL_strlen(newpw)); PORT_Free(newpw); } return rv; } struct matchobj { SECItem index; char *nname; PRBool found; }; char * SECU_DefaultSSLDir(void) { char *dir; static char sslDir[1000]; dir = PR_GetEnvSecure("SSL_DIR"); if (!dir) return NULL; if (strlen(dir) >= PR_ARRAY_SIZE(sslDir)) { return NULL; } 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_GetEnvSecure("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; } 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 & 0xFF)) goto loser; *d++ = (unsigned char)bmpChar; } secu_PrintRawString(out, &tmp, m, level); PORT_Free(tmp.data); return; loser: SECU_PrintAsHex(out, i, m, level); if (tmp.data) PORT_Free(tmp.data); } static void secu_PrintUniversal(FILE *out, const SECItem *i, const char *m, int level) { switch (i->data[0] & SEC_ASN1_TAGNUM_MASK) { case SEC_ASN1_ENUMERATED: case SEC_ASN1_INTEGER: SECU_PrintEncodedInteger(out, i, m, level); break; case SEC_ASN1_OBJECT_ID: SECU_PrintEncodedObjectID(out, i, m, level); break; case SEC_ASN1_BOOLEAN: SECU_PrintEncodedBoolean(out, i, m, level); break; case SEC_ASN1_UTF8_STRING: case SEC_ASN1_PRINTABLE_STRING: case SEC_ASN1_VISIBLE_STRING: case SEC_ASN1_IA5_STRING: case SEC_ASN1_T61_STRING: SECU_PrintString(out, i, m, level); break; case SEC_ASN1_GENERALIZED_TIME: SECU_PrintGeneralizedTime(out, i, m, level); break; case SEC_ASN1_UTC_TIME: SECU_PrintUTCTime(out, i, m, level); break; case SEC_ASN1_NULL: SECU_Indent(out, level); if (m && m[0]) fprintf(out, "%s: NULL\n", m); else fprintf(out, "NULL\n"); break; case SEC_ASN1_SET: case SEC_ASN1_SEQUENCE: SECU_PrintSet(out, i, m, level); break; case SEC_ASN1_OCTET_STRING: secu_PrintOctetString(out, i, m, level); break; case SEC_ASN1_BIT_STRING: secu_PrintBitString(out, i, m, level); break; case SEC_ASN1_BMP_STRING: secu_PrintBMPString(out, i, m, level); break; case SEC_ASN1_UNIVERSAL_STRING: secu_PrintUniversalString(out, i, m, level); break; default: SECU_PrintAsHex(out, i, m, level); break; } } void SECU_PrintAny(FILE *out, const SECItem *i, const char *m, int level) { if (i && i->len && i->data) { switch (i->data[0] & SEC_ASN1_CLASS_MASK) { case SEC_ASN1_CONTEXT_SPECIFIC: secu_PrintContextSpecific(out, i, m, level); break; case SEC_ASN1_UNIVERSAL: secu_PrintUniversal(out, i, m, level); break; default: SECU_PrintAsHex(out, i, m, level); break; } } } static int secu_PrintValidity(FILE *out, CERTValidity *v, char *m, int level) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); SECU_PrintTimeChoice(out, &v->notBefore, "Not Before", level + 1); SECU_PrintTimeChoice(out, &v->notAfter, "Not After ", level + 1); return 0; } /* This function does NOT expect a DER type and length. */ SECOidTag SECU_PrintObjectID(FILE *out, const SECItem *oid, const char *m, int level) { SECOidData *oiddata; char *oidString = NULL; oiddata = SECOID_FindOID(oid); if (oiddata != NULL) { const char *name = oiddata->desc; SECU_Indent(out, level); if (m != NULL) fprintf(out, "%s: ", m); fprintf(out, "%s\n", name); return oiddata->offset; } oidString = CERT_GetOidString(oid); if (oidString) { SECU_Indent(out, level); if (m != NULL) fprintf(out, "%s: ", m); fprintf(out, "%s\n", oidString); PR_smprintf_free(oidString); return SEC_OID_UNKNOWN; } SECU_PrintAsHex(out, oid, m, level); return SEC_OID_UNKNOWN; } typedef struct secuPBEParamsStr { SECItem salt; SECItem iterationCount; SECItem keyLength; SECAlgorithmID cipherAlg; SECAlgorithmID kdfAlg; } secuPBEParams; SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) /* SECOID_PKCS5_PBKDF2 */ const SEC_ASN1Template secuKDF2Params[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, { SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) }, { SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) }, { SEC_ASN1_INTEGER, offsetof(secuPBEParams, keyLength) }, { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, { 0 } }; /* PKCS5v1 & PKCS12 */ const SEC_ASN1Template secuPBEParamsTemp[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, { SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) }, { SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) }, { 0 } }; /* SEC_OID_PKCS5_PBES2, SEC_OID_PKCS5_PBMAC1 */ const SEC_ASN1Template secuPBEV2Params[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, cipherAlg), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, { 0 } }; void secu_PrintRSAPSSParams(FILE *out, SECItem *value, char *m, int level) { PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); SECStatus rv; SECKEYRSAPSSParams param; SECAlgorithmID maskHashAlg; if (m) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); } if (!pool) { SECU_Indent(out, level); fprintf(out, "Out of memory\n"); return; } PORT_Memset(¶m, 0, sizeof param); rv = SEC_QuickDERDecodeItem(pool, ¶m, SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate), value); if (rv == SECSuccess) { if (!param.hashAlg) { SECU_Indent(out, level + 1); fprintf(out, "Hash algorithm: default, SHA-1\n"); } else { SECU_PrintObjectID(out, ¶m.hashAlg->algorithm, "Hash algorithm", level + 1); } if (!param.maskAlg) { SECU_Indent(out, level + 1); fprintf(out, "Mask algorithm: default, MGF1\n"); SECU_Indent(out, level + 1); fprintf(out, "Mask hash algorithm: default, SHA-1\n"); } else { SECU_PrintObjectID(out, ¶m.maskAlg->algorithm, "Mask algorithm", level + 1); rv = SEC_QuickDERDecodeItem(pool, &maskHashAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), ¶m.maskAlg->parameters); if (rv == SECSuccess) { SECU_PrintObjectID(out, &maskHashAlg.algorithm, "Mask hash algorithm", level + 1); } else { SECU_Indent(out, level + 1); fprintf(out, "Invalid mask generation algorithm parameters\n"); } } if (!param.saltLength.data) { SECU_Indent(out, level + 1); fprintf(out, "Salt length: default, %i (0x%2X)\n", 20, 20); } else { SECU_PrintInteger(out, ¶m.saltLength, "Salt length", level + 1); } } else { SECU_Indent(out, level + 1); fprintf(out, "Invalid RSA-PSS parameters\n"); } PORT_FreeArena(pool, PR_FALSE); } void secu_PrintKDF2Params(FILE *out, SECItem *value, char *m, int level) { PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); SECStatus rv; secuPBEParams param; if (m) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); } if (!pool) { SECU_Indent(out, level); fprintf(out, "Out of memory\n"); return; } PORT_Memset(¶m, 0, sizeof param); rv = SEC_QuickDERDecodeItem(pool, ¶m, secuKDF2Params, value); if (rv == SECSuccess) { SECU_PrintAsHex(out, ¶m.salt, "Salt", level + 1); SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count", level + 1); SECU_PrintInteger(out, ¶m.keyLength, "Key Length", level + 1); SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF algorithm", level + 1); } PORT_FreeArena(pool, PR_FALSE); } void secu_PrintPKCS5V2Params(FILE *out, SECItem *value, char *m, int level) { PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); SECStatus rv; secuPBEParams param; if (m) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); } if (!pool) { SECU_Indent(out, level); fprintf(out, "Out of memory\n"); return; } PORT_Memset(¶m, 0, sizeof param); rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEV2Params, value); if (rv == SECSuccess) { SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF", level + 1); SECU_PrintAlgorithmID(out, ¶m.cipherAlg, "Cipher", level + 1); } PORT_FreeArena(pool, PR_FALSE); } void secu_PrintPBEParams(FILE *out, SECItem *value, char *m, int level) { PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); SECStatus rv; secuPBEParams param; if (m) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); } if (!pool) { SECU_Indent(out, level); fprintf(out, "Out of memory\n"); return; } PORT_Memset(¶m, 0, sizeof(secuPBEParams)); rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEParamsTemp, value); if (rv == SECSuccess) { SECU_PrintAsHex(out, ¶m.salt, "Salt", level + 1); SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count", level + 1); } PORT_FreeArena(pool, PR_FALSE); } /* This function does NOT expect a DER type and length. */ void SECU_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m, int level) { SECOidTag algtag; SECU_PrintObjectID(out, &a->algorithm, m, level); algtag = SECOID_GetAlgorithmTag(a); if (SEC_PKCS5IsAlgorithmPBEAlgTag(algtag)) { switch (algtag) { case SEC_OID_PKCS5_PBKDF2: secu_PrintKDF2Params(out, &a->parameters, "Parameters", level + 1); break; case SEC_OID_PKCS5_PBES2: secu_PrintPKCS5V2Params(out, &a->parameters, "Encryption", level + 1); break; case SEC_OID_PKCS5_PBMAC1: secu_PrintPKCS5V2Params(out, &a->parameters, "MAC", level + 1); break; default: secu_PrintPBEParams(out, &a->parameters, "Parameters", level + 1); break; } return; } if (a->parameters.len == 0 || (a->parameters.len == 2 && PORT_Memcmp(a->parameters.data, "\005\000", 2) == 0)) { /* No arguments or NULL argument */ } else if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1); } else { /* Print args to algorithm */ SECU_PrintAsHex(out, &a->parameters, "Args", level + 1); } } static void secu_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m, int level) { SECItem *value; int i; char om[100]; if (m) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); } /* * Should make this smarter; look at the type field and then decode * and print the value(s) appropriately! */ SECU_PrintObjectID(out, &(attr->type), "Type", level + 1); if (attr->values != NULL) { i = 0; while ((value = attr->values[i++]) != NULL) { 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; } } } } } 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); } } void SECU_PrintRSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); SECU_PrintInteger(out, &pk->u.rsa.modulus, "Modulus", level + 1); SECU_PrintInteger(out, &pk->u.rsa.publicExponent, "Exponent", level + 1); if (pk->u.rsa.publicExponent.len == 1 && pk->u.rsa.publicExponent.data[0] == 1) { SECU_Indent(out, level + 1); fprintf(out, "Error: INVALID RSA KEY!\n"); } } void SECU_PrintDSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level) { SECU_Indent(out, level); fprintf(out, "%s:\n", m); SECU_PrintInteger(out, &pk->u.dsa.params.prime, "Prime", level + 1); SECU_PrintInteger(out, &pk->u.dsa.params.subPrime, "Subprime", level + 1); SECU_PrintInteger(out, &pk->u.dsa.params.base, "Base", level + 1); SECU_PrintInteger(out, &pk->u.dsa.publicValue, "PublicValue", level + 1); } static void secu_PrintSubjectPublicKeyInfo(FILE *out, PLArenaPool *arena, CERTSubjectPublicKeyInfo *i, char *msg, int level) { SECKEYPublicKey *pk; SECU_Indent(out, level); fprintf(out, "%s:\n", msg); SECU_PrintAlgorithmID(out, &i->algorithm, "Public Key Algorithm", level + 1); pk = SECKEY_ExtractPublicKey(i); if (pk) { switch (pk->keyType) { case rsaKey: SECU_PrintRSAPublicKey(out, pk, "RSA Public Key", level + 1); break; case dsaKey: SECU_PrintDSAPublicKey(out, pk, "DSA Public Key", level + 1); break; case ecKey: secu_PrintECPublicKey(out, pk, "EC Public Key", level + 1); break; case dhKey: case fortezzaKey: case keaKey: SECU_Indent(out, level); fprintf(out, "unable to format this SPKI algorithm type\n"); goto loser; default: SECU_Indent(out, level); fprintf(out, "unknown SPKI algorithm type\n"); goto loser; } PORT_FreeArena(pk->arena, PR_FALSE); } else { SECU_PrintErrMsg(out, level, "Error", "Parsing public key"); loser: if (i->subjectPublicKey.data) { SECU_PrintAny(out, &i->subjectPublicKey, "Raw", level); } } } static void printStringWithoutCRLF(FILE *out, const char *str) { const char *c = str; while (*c) { if (*c != '\r' && *c != '\n') { fputc(*c, out); } ++c; } } int SECU_PrintDumpDerIssuerAndSerial(FILE *out, SECItem *der, char *m, int level) { PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); CERTCertificate *c; int rv = SEC_ERROR_NO_MEMORY; char *derIssuerB64; char *derSerialB64; if (!arena) return rv; /* Decode certificate */ c = PORT_ArenaZNew(arena, CERTCertificate); if (!c) goto loser; c->arena = arena; rv = SEC_ASN1DecodeItem(arena, c, SEC_ASN1_GET(CERT_CertificateTemplate), der); if (rv) { SECU_PrintErrMsg(out, 0, "Error", "Parsing extension"); goto loser; } SECU_PrintName(out, &c->subject, "Subject", 0); if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/ SECU_Newline(out); SECU_PrintName(out, &c->issuer, "Issuer", 0); if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/ SECU_Newline(out); SECU_PrintInteger(out, &c->serialNumber, "Serial Number", 0); derIssuerB64 = BTOA_ConvertItemToAscii(&c->derIssuer); derSerialB64 = BTOA_ConvertItemToAscii(&c->serialNumber); fprintf(out, "Issuer DER Base64:\n"); if (SECU_GetWrapEnabled()) { fprintf(out, "%s\n", derIssuerB64); } else { printStringWithoutCRLF(out, derIssuerB64); fputs("\n", out); } fprintf(out, "Serial DER Base64:\n"); if (SECU_GetWrapEnabled()) { fprintf(out, "%s\n", derSerialB64); } else { printStringWithoutCRLF(out, derSerialB64); fputs("\n", out); } PORT_Free(derIssuerB64); PORT_Free(derSerialB64); fprintf(out, "Serial DER as C source: \n{ %d, \"", c->serialNumber.len); { unsigned int i; for (i = 0; i < c->serialNumber.len; ++i) { unsigned char *chardata = (unsigned char *)(c->serialNumber.data); unsigned char ch = *(chardata + i); fprintf(out, "\\x%02x", ch); } fprintf(out, "\" }\n"); } loser: PORT_FreeArena(arena, PR_FALSE); return rv; } static SECStatus secu_PrintX509InvalidDate(FILE *out, SECItem *value, char *msg, int level) { SECItem decodedValue; SECStatus rv; PRTime invalidTime; char *formattedTime = NULL; decodedValue.data = NULL; rv = SEC_ASN1DecodeItem(NULL, &decodedValue, SEC_ASN1_GET(SEC_GeneralizedTimeTemplate), value); if (rv == SECSuccess) { rv = DER_GeneralizedTimeToTime(&invalidTime, &decodedValue); if (rv == SECSuccess) { formattedTime = CERT_GenTime2FormattedAscii(invalidTime, "%a %b %d %H:%M:%S %Y"); SECU_Indent(out, level + 1); fprintf(out, "%s: %s\n", msg, formattedTime); PORT_Free(formattedTime); } } PORT_Free(decodedValue.data); return (rv); } static SECStatus PrintExtKeyUsageExtension(FILE *out, SECItem *value, char *msg, int level) { 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, "\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 signatureOption) { 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 (signatureOption == 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; PK11SlotList *slotList; PRBool falseAttributeFound = PR_FALSE; PRBool trueAttributeFound = PR_FALSE; const char *moz_policy_ca_info = NULL; 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); } slotList = PK11_GetAllSlotsForCert(cert, NULL); if (slotList) { PK11SlotListElement *se = PK11_GetFirstSafe(slotList); for (; se; se = PK11_GetNextSafe(slotList, se, PR_FALSE)) { CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(se->slot, cert, NULL); if (handle != CK_INVALID_HANDLE) { PORT_SetError(0); if (PK11_HasAttributeSet(se->slot, handle, CKA_NSS_MOZILLA_CA_POLICY, PR_FALSE)) { trueAttributeFound = PR_TRUE; } else if (!PORT_GetError()) { falseAttributeFound = PR_TRUE; } } } PK11_FreeSlotList(slotList); } if (trueAttributeFound) { moz_policy_ca_info = "true (attribute present)"; } else if (falseAttributeFound) { moz_policy_ca_info = "false (attribute present)"; } else { moz_policy_ca_info = "false (attribute missing)"; } SECU_Indent(stdout, 1); printf("Mozilla-CA-Policy: %s\n", moz_policy_ca_info); 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; } 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; } 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: 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 != SECSuccess) { goto loser; } /* Fill out SignedData object */ PORT_Memset(sd, 0, sizeof(*sd)); sd->data.data = buf; sd->data.len = len; rv = SECITEM_CopyItem(arena, &sd->signature, &it); if (rv != SECSuccess) { goto loser; } sd->signature.len <<= 3; /* convert to bit string */ rv = SECOID_SetAlgorithmID(arena, &sd->signatureAlgorithm, algID, 0); if (rv != SECSuccess) { goto loser; } loser: PORT_Free(it.data); return rv; } /* * 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_FindCertByNicknameOrEmailAddrCX(handle, name, pwarg); 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, "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; } if (!PL_strncasecmp(buf, "tls1.3", bufLen)) { *version = SSL_LIBRARY_VERSION_TLS_1_3; return SECSuccess; } PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } SECStatus SECU_ParseSSLVersionRangeString(const char *input, const SSLVersionRange defaultVersionRange, SSLVersionRange *vrange) { const char *colonPos; size_t colonIndex; const char *maxStr; if (!input || !vrange) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } // We don't support SSL2 any longer. if (defaultVersionRange.min < SSL_LIBRARY_VERSION_3_0 || defaultVersionRange.max < SSL_LIBRARY_VERSION_3_0) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (!strcmp(input, ":")) { /* special value, use default */ *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 */ 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; } 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; } vrange->max = version; } if (vrange->min > vrange->max) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } return SECSuccess; } SSLNamedGroup groupNameToNamedGroup(char *name) { if (PL_strlen(name) == 4) { if (!strncmp(name, "P256", 4)) { return ssl_grp_ec_secp256r1; } if (!strncmp(name, "P384", 4)) { return ssl_grp_ec_secp384r1; } if (!strncmp(name, "P521", 4)) { return ssl_grp_ec_secp521r1; } } if (PL_strlen(name) == 6) { if (!strncmp(name, "x25519", 6)) { return ssl_grp_ec_curve25519; } if (!strncmp(name, "FF2048", 6)) { return ssl_grp_ffdhe_2048; } if (!strncmp(name, "FF3072", 6)) { return ssl_grp_ffdhe_3072; } if (!strncmp(name, "FF4096", 6)) { return ssl_grp_ffdhe_4096; } if (!strncmp(name, "FF6144", 6)) { return ssl_grp_ffdhe_6144; } if (!strncmp(name, "FF8192", 6)) { return ssl_grp_ffdhe_8192; } } return ssl_grp_none; } SECStatus parseGroupList(const char *arg, SSLNamedGroup **enabledGroups, unsigned int *enabledGroupsCount) { SSLNamedGroup *groups; char *str; char *p; unsigned int numValues = 0; unsigned int count = 0; /* Count the number of groups. */ str = PORT_Strdup(arg); if (!str) { return SECFailure; } p = strtok(str, ","); while (p) { ++numValues; p = strtok(NULL, ","); } PORT_Free(str); str = NULL; groups = PORT_ZNewArray(SSLNamedGroup, numValues); if (!groups) { goto done; } /* Get group names. */ str = PORT_Strdup(arg); if (!str) { goto done; } p = strtok(str, ","); while (p) { SSLNamedGroup group = groupNameToNamedGroup(p); if (group == ssl_grp_none) { count = 0; goto done; } groups[count++] = group; p = strtok(NULL, ","); } done: if (str) { PORT_Free(str); } if (!count) { PORT_Free(groups); return SECFailure; } *enabledGroupsCount = count; *enabledGroups = groups; return SECSuccess; }