| /* 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> |
| #include <limits.h> |
| |
| #ifdef XP_UNIX |
| #include <unistd.h> |
| #endif |
| |
| /* for SEC_TraverseNames */ |
| #include "cert.h" |
| #include "certt.h" |
| #include "certdb.h" |
| |
| #include "secmod.h" |
| #include "pk11func.h" |
| #include "secoid.h" |
| |
| static char consoleName[] = { |
| #ifdef XP_UNIX |
| "/dev/tty" |
| #else |
| #ifdef XP_OS2 |
| "\\DEV\\CON" |
| #else |
| "CON:" |
| #endif |
| #endif |
| }; |
| |
| #include "nssutil.h" |
| #include "ssl.h" |
| #include "sslproto.h" |
| |
| static PRBool utf8DisplayEnabled = PR_FALSE; |
| |
| /* The minimum password/pin length (in Unicode characters) in FIPS mode, |
| * defined in lib/softoken/pkcs11i.h. */ |
| #define FIPS_MIN_PIN 7 |
| |
| void |
| SECU_EnableUtf8Display(PRBool enable) |
| { |
| utf8DisplayEnabled = enable; |
| } |
| |
| PRBool |
| SECU_GetUtf8DisplayEnabled(void) |
| { |
| return utf8DisplayEnabled; |
| } |
| |
| static void |
| secu_ClearPassword(char *p) |
| { |
| if (p) { |
| PORT_Memset(p, 0, PORT_Strlen(p)); |
| PORT_Free(p); |
| } |
| } |
| |
| char * |
| SECU_GetPasswordString(void *arg, char *prompt) |
| { |
| #ifndef _WINDOWS |
| char *p = NULL; |
| FILE *input, *output; |
| |
| /* open terminal */ |
| input = fopen(consoleName, "r"); |
| if (input == NULL) { |
| fprintf(stderr, "Error opening input terminal for read\n"); |
| return NULL; |
| } |
| |
| output = fopen(consoleName, "w"); |
| if (output == NULL) { |
| fprintf(stderr, "Error opening output terminal for write\n"); |
| fclose(input); |
| return NULL; |
| } |
| |
| p = SEC_GetPassword(input, output, prompt, SEC_BlindCheckPassword); |
| |
| fclose(input); |
| fclose(output); |
| |
| return p; |
| |
| #else |
| /* Win32 version of above. opening the console may fail |
| on windows95, and certainly isn't necessary.. */ |
| |
| char *p = NULL; |
| |
| p = SEC_GetPassword(stdin, stdout, prompt, SEC_BlindCheckPassword); |
| return p; |
| |
| #endif |
| } |
| |
| /* |
| * p a s s w o r d _ h a r d c o d e |
| * |
| * A function to use the password passed in the -f(pwfile) argument |
| * of the command line. |
| * After use once, null it out otherwise PKCS11 calls us forever.? |
| * |
| */ |
| char * |
| SECU_FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg) |
| { |
| char *phrases, *phrase; |
| PRFileDesc *fd; |
| PRInt32 nb; |
| char *pwFile = arg; |
| int i; |
| const long maxPwdFileSize = 4096; |
| char *tokenName = NULL; |
| int tokenLen = 0; |
| |
| if (!pwFile) |
| return 0; |
| |
| if (retry) { |
| return 0; /* no good retrying - the files contents will be the same */ |
| } |
| |
| phrases = PORT_ZAlloc(maxPwdFileSize); |
| |
| if (!phrases) { |
| return 0; /* out of memory */ |
| } |
| |
| fd = PR_Open(pwFile, PR_RDONLY, 0); |
| if (!fd) { |
| fprintf(stderr, "No password file \"%s\" exists.\n", pwFile); |
| PORT_Free(phrases); |
| return NULL; |
| } |
| |
| nb = PR_Read(fd, phrases, maxPwdFileSize); |
| |
| PR_Close(fd); |
| |
| if (nb == 0) { |
| fprintf(stderr, "password file contains no data\n"); |
| PORT_Free(phrases); |
| return NULL; |
| } |
| |
| if (slot) { |
| tokenName = PK11_GetTokenName(slot); |
| if (tokenName) { |
| tokenLen = PORT_Strlen(tokenName); |
| } |
| } |
| i = 0; |
| do { |
| int startphrase = i; |
| int phraseLen; |
| |
| /* handle the Windows EOL case */ |
| while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) |
| i++; |
| /* terminate passphrase */ |
| phrases[i++] = '\0'; |
| /* clean up any EOL before the start of the next passphrase */ |
| while ((i < nb) && (phrases[i] == '\r' || phrases[i] == '\n')) { |
| phrases[i++] = '\0'; |
| } |
| /* now analyze the current passphrase */ |
| phrase = &phrases[startphrase]; |
| if (!tokenName) |
| break; |
| if (PORT_Strncmp(phrase, tokenName, tokenLen)) |
| continue; |
| phraseLen = PORT_Strlen(phrase); |
| if (phraseLen < (tokenLen + 1)) |
| continue; |
| if (phrase[tokenLen] != ':') |
| continue; |
| phrase = &phrase[tokenLen + 1]; |
| break; |
| |
| } while (i < nb); |
| |
| phrase = PORT_Strdup((char *)phrase); |
| PORT_Free(phrases); |
| return phrase; |
| } |
| |
| char * |
| SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg) |
| { |
| char prompt[255]; |
| secuPWData *pwdata = (secuPWData *)arg; |
| secuPWData pwnull = { PW_NONE, 0 }; |
| secuPWData pwxtrn = { PW_EXTERNAL, "external" }; |
| |
| if (pwdata == NULL) |
| pwdata = &pwnull; |
| |
| if (PK11_ProtectedAuthenticationPath(slot)) { |
| pwdata = &pwxtrn; |
| } |
| if (retry && pwdata->source != PW_NONE) { |
| PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n"); |
| return NULL; |
| } |
| |
| switch (pwdata->source) { |
| case PW_NONE: |
| 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; |
| |
| /* Read in ascii data */ |
| rv = SECU_FileToItem(&filedata, inFile); |
| if (rv != SECSuccess) |
| return rv; |
| if (!filedata.data) { |
| fprintf(stderr, "unable to read data from input file\n"); |
| return SECFailure; |
| } |
| /* need one additional byte for zero terminator */ |
| rv = SECITEM_ReallocItemV2(NULL, &filedata, filedata.len + 1); |
| if (rv != SECSuccess) { |
| PORT_Free(filedata.data); |
| return rv; |
| } |
| char *asc = (char *)filedata.data; |
| asc[filedata.len - 1] = '\0'; |
| |
| if (warnOnPrivateKeyInAsciiFile && strstr(asc, "PRIVATE KEY")) { |
| fprintf(stderr, "Warning: ignoring private key. Consider to use " |
| "pk12util.\n"); |
| } |
| |
| char *body; |
| /* check for headers and trailers and remove them */ |
| if ((body = strstr(asc, "-----BEGIN")) != NULL) { |
| char *trailer = NULL; |
| asc = body; |
| body = PORT_Strchr(body, '\n'); |
| if (!body) |
| body = PORT_Strchr(asc, '\r'); /* maybe this is a MAC file */ |
| if (body) |
| trailer = strstr(++body, "-----END"); |
| if (trailer != NULL) { |
| *trailer = '\0'; |
| } else { |
| fprintf(stderr, "input has header but no trailer\n"); |
| PORT_Free(filedata.data); |
| return SECFailure; |
| } |
| } else { |
| body = asc; |
| } |
| |
| /* Convert to binary */ |
| rv = ATOB_ConvertAsciiToItem(der, body); |
| if (rv != SECSuccess) { |
| fprintf(stderr, "error converting ascii to binary (%s)\n", |
| SECU_Strerror(PORT_GetError())); |
| PORT_Free(filedata.data); |
| return SECFailure; |
| } |
| |
| PORT_Free(filedata.data); |
| } else { |
| /* Read in binary der */ |
| rv = SECU_FileToItem(der, inFile); |
| if (rv != SECSuccess) { |
| fprintf(stderr, "error converting der (%s)\n", |
| SECU_Strerror(PORT_GetError())); |
| return SECFailure; |
| } |
| } |
| return SECSuccess; |
| } |
| |
| #define INDENT_MULT 4 |
| |
| /* |
| * remove the tag and length and just leave the bare BER data |
| */ |
| SECStatus |
| SECU_StripTagAndLength(SECItem *i) |
| { |
| unsigned int start; |
| PRBool isIndefinite; |
| |
| if (!i || !i->data || i->len < 2) { /* must be at least tag and length */ |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| isIndefinite = (i->data[1] == 0x80); |
| start = ((i->data[1] & 0x80) ? (i->data[1] & 0x7f) + 2 : 2); |
| if (i->len < start) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| i->data += start; |
| i->len -= start; |
| /* we are using indefinite encoding, drop the trailing zero */ |
| if (isIndefinite) { |
| if (i->len <= 1) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| /* verify tags are zero */ |
| if ((i->data[i->len - 1] != 0) || (i->data[i->len - 2] != 0)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| i->len -= 2; |
| } |
| |
| return SECSuccess; |
| } |
| |
| /* |
| * Create a new SECItem which points to the current BER tag and length with |
| * all it's data. For indefinite encoding, this will also include the trailing |
| * indefinite markers |
| * The 'in' item is advanced to point to the next BER tag. |
| * You don't want to use this in an actual BER/DER parser as NSS already |
| * has 3 to choose from) |
| */ |
| SECStatus |
| SECU_ExtractBERAndStep(SECItem *in, SECItem *out) |
| { |
| if (!in || !in->data || in->len < 2) { /* must be at least tag and length */ |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| |
| *out = *in; |
| |
| /* first handle indefinite encoding */ |
| if (out->data[1] == 0x80) { |
| SECItem this = *out; |
| SECItem next; |
| this.data += 2; |
| this.len -= 2; |
| out->len = 2; |
| /* walk through all the entries until we find the '0' */ |
| while ((this.len >= 2) && (this.data[0] != 0)) { |
| SECStatus rv = SECU_ExtractBERAndStep(&this, &next); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| out->len += next.len; |
| } |
| if ((this.len < 2) || ((this.data[0] != 0) && (this.data[1] != 0))) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| out->len += 2; /* include the trailing zeros */ |
| in->data += out->len; |
| in->len -= out->len; |
| return SECSuccess; |
| } |
| |
| /* now handle normal DER encoding */ |
| if (out->data[1] & 0x80) { |
| unsigned int i; |
| unsigned int lenlen = out->data[1] & 0x7f; |
| unsigned int len = 0; |
| if (lenlen > sizeof out->len) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| for (i = 0; i < lenlen; i++) { |
| len = (len << 8) | out->data[2 + i]; |
| } |
| out->len = len + lenlen + 2; |
| } else { |
| out->len = out->data[1] + 2; |
| } |
| if (out->len > in->len) { |
| /* we've ran into a truncated file */ |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| in->data += out->len; |
| in->len -= out->len; |
| return SECSuccess; |
| } |
| |
| static void |
| secu_PrintRawStringQuotesOptional(FILE *out, SECItem *si, const char *m, |
| int level, PRBool quotes) |
| { |
| int column; |
| unsigned int i; |
| |
| if (m) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s: ", m); |
| column = (level * INDENT_MULT) + strlen(m) + 2; |
| level++; |
| } else { |
| SECU_Indent(out, level); |
| column = level * INDENT_MULT; |
| } |
| if (quotes) { |
| fprintf(out, "\""); |
| column++; |
| } |
| |
| for (i = 0; i < si->len; i++) { |
| unsigned char val = si->data[i]; |
| unsigned char c; |
| if (SECU_GetWrapEnabled() && column > 76) { |
| SECU_Newline(out); |
| SECU_Indent(out, level); |
| column = level * INDENT_MULT; |
| } |
| |
| if (utf8DisplayEnabled) { |
| if (val < 32) |
| c = '.'; |
| else |
| c = val; |
| } else { |
| c = printable[val]; |
| } |
| fprintf(out, "%c", c); |
| column++; |
| } |
| |
| if (quotes) { |
| fprintf(out, "\""); |
| column++; |
| } |
| if (SECU_GetWrapEnabled() && |
| (column != level * INDENT_MULT || column > 76)) { |
| SECU_Newline(out); |
| } |
| } |
| |
| static void |
| secu_PrintRawString(FILE *out, SECItem *si, const char *m, int level) |
| { |
| secu_PrintRawStringQuotesOptional(out, si, m, level, PR_TRUE); |
| } |
| |
| void |
| SECU_PrintString(FILE *out, const SECItem *si, const char *m, int level) |
| { |
| SECItem my = *si; |
| |
| if (SECSuccess != SECU_StripTagAndLength(&my) || !my.len) |
| return; |
| secu_PrintRawString(out, &my, m, level); |
| } |
| |
| /* print an unencoded boolean */ |
| static void |
| secu_PrintBoolean(FILE *out, SECItem *i, const char *m, int level) |
| { |
| int val = 0; |
| |
| if (i->data && i->len) { |
| val = i->data[0]; |
| } |
| |
| if (!m) { |
| m = "Boolean"; |
| } |
| SECU_Indent(out, level); |
| fprintf(out, "%s: %s\n", m, (val ? "True" : "False")); |
| } |
| |
| /* |
| * Format and print "time". If the tag message "m" is not NULL, |
| * do indent formatting based on "level" and add a newline afterward; |
| * otherwise just print the formatted time string only. |
| */ |
| static void |
| secu_PrintTime(FILE *out, const PRTime time, const char *m, int level) |
| { |
| PRExplodedTime printableTime; |
| char *timeString; |
| |
| /* Convert to local time */ |
| PR_ExplodeTime(time, PR_GMTParameters, &printableTime); |
| |
| timeString = PORT_Alloc(256); |
| if (timeString == NULL) |
| return; |
| |
| if (m != NULL) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s: ", m); |
| } |
| |
| if (PR_FormatTime(timeString, 256, "%a %b %d %H:%M:%S %Y", &printableTime)) { |
| fputs(timeString, out); |
| } |
| |
| if (m != NULL) |
| fprintf(out, "\n"); |
| |
| PORT_Free(timeString); |
| } |
| |
| /* |
| * Format and print the UTC Time "t". If the tag message "m" is not NULL, |
| * do indent formatting based on "level" and add a newline afterward; |
| * otherwise just print the formatted time string only. |
| */ |
| void |
| SECU_PrintUTCTime(FILE *out, const SECItem *t, const char *m, int level) |
| { |
| PRTime time; |
| SECStatus rv; |
| |
| rv = DER_UTCTimeToTime(&time, t); |
| if (rv != SECSuccess) |
| return; |
| |
| secu_PrintTime(out, time, m, level); |
| } |
| |
| /* |
| * Format and print the Generalized Time "t". If the tag message "m" |
| * is not NULL, * do indent formatting based on "level" and add a newline |
| * afterward; otherwise just print the formatted time string only. |
| */ |
| void |
| SECU_PrintGeneralizedTime(FILE *out, const SECItem *t, const char *m, int level) |
| { |
| PRTime time; |
| SECStatus rv; |
| |
| rv = DER_GeneralizedTimeToTime(&time, t); |
| if (rv != SECSuccess) |
| return; |
| |
| secu_PrintTime(out, time, m, level); |
| } |
| |
| /* |
| * Format and print the UTC or Generalized Time "t". If the tag message |
| * "m" is not NULL, do indent formatting based on "level" and add a newline |
| * afterward; otherwise just print the formatted time string only. |
| */ |
| void |
| SECU_PrintTimeChoice(FILE *out, const SECItem *t, const char *m, int level) |
| { |
| switch (t->type) { |
| case siUTCTime: |
| SECU_PrintUTCTime(out, t, m, level); |
| break; |
| |
| case siGeneralizedTime: |
| SECU_PrintGeneralizedTime(out, t, m, level); |
| break; |
| |
| default: |
| PORT_Assert(0); |
| break; |
| } |
| } |
| |
| /* This prints a SET or SEQUENCE */ |
| static void |
| SECU_PrintSet(FILE *out, const SECItem *t, const char *m, int level) |
| { |
| int type = t->data[0] & SEC_ASN1_TAGNUM_MASK; |
| int constructed = t->data[0] & SEC_ASN1_CONSTRUCTED; |
| const char *label; |
| SECItem my = *t; |
| |
| if (!constructed) { |
| SECU_PrintAsHex(out, t, m, level); |
| return; |
| } |
| if (SECSuccess != SECU_StripTagAndLength(&my)) |
| return; |
| |
| SECU_Indent(out, level); |
| if (m) { |
| fprintf(out, "%s: ", m); |
| } |
| |
| if (type == SEC_ASN1_SET) |
| label = "Set "; |
| else if (type == SEC_ASN1_SEQUENCE) |
| label = "Sequence "; |
| else |
| label = ""; |
| fprintf(out, "%s{\n", label); /* } */ |
| |
| while (my.len >= 2) { |
| SECItem tmp; |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &tmp)) { |
| break; |
| } |
| SECU_PrintAny(out, &tmp, NULL, level + 1); |
| } |
| SECU_Indent(out, level); |
| fprintf(out, /* { */ "}\n"); |
| } |
| |
| static void |
| secu_PrintContextSpecific(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| int type = i->data[0] & SEC_ASN1_TAGNUM_MASK; |
| int constructed = i->data[0] & SEC_ASN1_CONSTRUCTED; |
| SECItem tmp; |
| |
| if (constructed) { |
| char *m2; |
| if (!m) |
| m2 = PR_smprintf("[%d]", type); |
| else |
| m2 = PR_smprintf("%s: [%d]", m, type); |
| if (m2) { |
| SECU_PrintSet(out, i, m2, level); |
| PR_smprintf_free(m2); |
| } |
| return; |
| } |
| |
| SECU_Indent(out, level); |
| if (m) { |
| fprintf(out, "%s: ", m); |
| } |
| fprintf(out, "[%d]\n", type); |
| |
| tmp = *i; |
| if (SECSuccess == SECU_StripTagAndLength(&tmp)) |
| SECU_PrintAsHex(out, &tmp, m, level + 1); |
| } |
| |
| static void |
| secu_PrintOctetString(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| SECItem tmp = *i; |
| if (SECSuccess == SECU_StripTagAndLength(&tmp)) |
| SECU_PrintAsHex(out, &tmp, m, level); |
| } |
| |
| static void |
| secu_PrintBitString(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| int unused_bits; |
| SECItem tmp = *i; |
| |
| if (SECSuccess != SECU_StripTagAndLength(&tmp) || tmp.len < 2) |
| return; |
| |
| unused_bits = *tmp.data++; |
| tmp.len--; |
| |
| SECU_PrintAsHex(out, &tmp, m, level); |
| if (unused_bits) { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "(%d least significant bits unused)\n", unused_bits); |
| } |
| } |
| |
| /* in a decoded bit string, the len member is a bit length. */ |
| static void |
| secu_PrintDecodedBitString(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| int unused_bits; |
| SECItem tmp = *i; |
| |
| unused_bits = (tmp.len & 0x7) ? 8 - (tmp.len & 7) : 0; |
| DER_ConvertBitString(&tmp); /* convert length to byte length */ |
| |
| SECU_PrintAsHex(out, &tmp, m, level); |
| if (unused_bits) { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "(%d least significant bits unused)\n", unused_bits); |
| } |
| } |
| |
| /* Print a DER encoded Boolean */ |
| void |
| SECU_PrintEncodedBoolean(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| SECItem my = *i; |
| if (SECSuccess == SECU_StripTagAndLength(&my)) |
| secu_PrintBoolean(out, &my, m, level); |
| } |
| |
| /* Print a DER encoded integer */ |
| void |
| SECU_PrintEncodedInteger(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| SECItem my = *i; |
| if (SECSuccess == SECU_StripTagAndLength(&my)) |
| SECU_PrintInteger(out, &my, m, level); |
| } |
| |
| /* Print a DER encoded OID */ |
| SECOidTag |
| SECU_PrintEncodedObjectID(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| SECItem my = *i; |
| SECOidTag tag = SEC_OID_UNKNOWN; |
| if (SECSuccess == SECU_StripTagAndLength(&my)) |
| tag = SECU_PrintObjectID(out, &my, m, level); |
| return tag; |
| } |
| |
| static void |
| secu_PrintBMPString(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| unsigned char *s; |
| unsigned char *d; |
| int len; |
| SECItem tmp = { 0, 0, 0 }; |
| SECItem my = *i; |
| |
| if (SECSuccess != SECU_StripTagAndLength(&my)) |
| goto loser; |
| if (my.len % 2) |
| goto loser; |
| len = (int)(my.len / 2); |
| tmp.data = (unsigned char *)PORT_Alloc(len); |
| if (!tmp.data) |
| goto loser; |
| tmp.len = len; |
| for (s = my.data, d = tmp.data; len > 0; len--) { |
| PRUint32 bmpChar = (s[0] << 8) | s[1]; |
| s += 2; |
| if (!isprint(bmpChar)) |
| goto loser; |
| *d++ = (unsigned char)bmpChar; |
| } |
| secu_PrintRawString(out, &tmp, m, level); |
| PORT_Free(tmp.data); |
| return; |
| |
| loser: |
| SECU_PrintAsHex(out, i, m, level); |
| if (tmp.data) |
| PORT_Free(tmp.data); |
| } |
| |
| static void |
| secu_PrintUniversalString(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| unsigned char *s; |
| unsigned char *d; |
| int len; |
| SECItem tmp = { 0, 0, 0 }; |
| SECItem my = *i; |
| |
| if (SECSuccess != SECU_StripTagAndLength(&my)) |
| goto loser; |
| if (my.len % 4) |
| goto loser; |
| len = (int)(my.len / 4); |
| tmp.data = (unsigned char *)PORT_Alloc(len); |
| if (!tmp.data) |
| goto loser; |
| tmp.len = len; |
| for (s = my.data, d = tmp.data; len > 0; len--) { |
| PRUint32 bmpChar = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; |
| s += 4; |
| if (!isprint(bmpChar & 0xFF)) |
| goto loser; |
| *d++ = (unsigned char)bmpChar; |
| } |
| secu_PrintRawString(out, &tmp, m, level); |
| PORT_Free(tmp.data); |
| return; |
| |
| loser: |
| SECU_PrintAsHex(out, i, m, level); |
| if (tmp.data) |
| PORT_Free(tmp.data); |
| } |
| |
| static void |
| secu_PrintUniversal(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| switch (i->data[0] & SEC_ASN1_TAGNUM_MASK) { |
| case SEC_ASN1_ENUMERATED: |
| case SEC_ASN1_INTEGER: |
| SECU_PrintEncodedInteger(out, i, m, level); |
| break; |
| case SEC_ASN1_OBJECT_ID: |
| SECU_PrintEncodedObjectID(out, i, m, level); |
| break; |
| case SEC_ASN1_BOOLEAN: |
| SECU_PrintEncodedBoolean(out, i, m, level); |
| break; |
| case SEC_ASN1_UTF8_STRING: |
| case SEC_ASN1_PRINTABLE_STRING: |
| case SEC_ASN1_VISIBLE_STRING: |
| case SEC_ASN1_IA5_STRING: |
| case SEC_ASN1_T61_STRING: |
| SECU_PrintString(out, i, m, level); |
| break; |
| case SEC_ASN1_GENERALIZED_TIME: |
| SECU_PrintGeneralizedTime(out, i, m, level); |
| break; |
| case SEC_ASN1_UTC_TIME: |
| SECU_PrintUTCTime(out, i, m, level); |
| break; |
| case SEC_ASN1_NULL: |
| SECU_Indent(out, level); |
| if (m && m[0]) |
| fprintf(out, "%s: NULL\n", m); |
| else |
| fprintf(out, "NULL\n"); |
| break; |
| case SEC_ASN1_SET: |
| case SEC_ASN1_SEQUENCE: |
| SECU_PrintSet(out, i, m, level); |
| break; |
| case SEC_ASN1_OCTET_STRING: |
| secu_PrintOctetString(out, i, m, level); |
| break; |
| case SEC_ASN1_BIT_STRING: |
| secu_PrintBitString(out, i, m, level); |
| break; |
| case SEC_ASN1_BMP_STRING: |
| secu_PrintBMPString(out, i, m, level); |
| break; |
| case SEC_ASN1_UNIVERSAL_STRING: |
| secu_PrintUniversalString(out, i, m, level); |
| break; |
| default: |
| SECU_PrintAsHex(out, i, m, level); |
| break; |
| } |
| } |
| |
| void |
| SECU_PrintAny(FILE *out, const SECItem *i, const char *m, int level) |
| { |
| if (i && i->len && i->data) { |
| switch (i->data[0] & SEC_ASN1_CLASS_MASK) { |
| case SEC_ASN1_CONTEXT_SPECIFIC: |
| secu_PrintContextSpecific(out, i, m, level); |
| break; |
| case SEC_ASN1_UNIVERSAL: |
| secu_PrintUniversal(out, i, m, level); |
| break; |
| default: |
| SECU_PrintAsHex(out, i, m, level); |
| break; |
| } |
| } |
| } |
| |
| static int |
| secu_PrintValidity(FILE *out, CERTValidity *v, char *m, int level) |
| { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| SECU_PrintTimeChoice(out, &v->notBefore, "Not Before", level + 1); |
| SECU_PrintTimeChoice(out, &v->notAfter, "Not After ", level + 1); |
| return 0; |
| } |
| |
| /* This function does NOT expect a DER type and length. */ |
| SECOidTag |
| SECU_PrintObjectID(FILE *out, const SECItem *oid, const char *m, int level) |
| { |
| SECOidData *oiddata; |
| char *oidString = NULL; |
| |
| oiddata = SECOID_FindOID(oid); |
| if (oiddata != NULL) { |
| const char *name = oiddata->desc; |
| SECU_Indent(out, level); |
| if (m != NULL) |
| fprintf(out, "%s: ", m); |
| fprintf(out, "%s\n", name); |
| return oiddata->offset; |
| } |
| oidString = CERT_GetOidString(oid); |
| if (oidString) { |
| SECU_Indent(out, level); |
| if (m != NULL) |
| fprintf(out, "%s: ", m); |
| fprintf(out, "%s\n", oidString); |
| PR_smprintf_free(oidString); |
| return SEC_OID_UNKNOWN; |
| } |
| SECU_PrintAsHex(out, oid, m, level); |
| return SEC_OID_UNKNOWN; |
| } |
| |
| typedef struct secuPBEParamsStr { |
| SECItem salt; |
| SECItem iterationCount; |
| SECItem keyLength; |
| SECAlgorithmID cipherAlg; |
| SECAlgorithmID kdfAlg; |
| } secuPBEParams; |
| |
| SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) |
| |
| /* SECOID_PKCS5_PBKDF2 */ |
| const SEC_ASN1Template secuKDF2Params[] = { |
| { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, |
| { SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) }, |
| { SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) }, |
| { SEC_ASN1_INTEGER, offsetof(secuPBEParams, keyLength) }, |
| { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg), |
| SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
| { 0 } |
| }; |
| |
| /* PKCS5v1 & PKCS12 */ |
| const SEC_ASN1Template secuPBEParamsTemp[] = { |
| { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, |
| { SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) }, |
| { SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) }, |
| { 0 } |
| }; |
| |
| /* SEC_OID_PKCS5_PBES2, SEC_OID_PKCS5_PBMAC1 */ |
| const SEC_ASN1Template secuPBEV2Params[] = { |
| { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, |
| { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg), |
| SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
| { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, cipherAlg), |
| SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
| { 0 } |
| }; |
| |
| void |
| secu_PrintRSAPSSParams(FILE *out, SECItem *value, char *m, int level) |
| { |
| PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| SECStatus rv; |
| SECKEYRSAPSSParams param; |
| SECAlgorithmID maskHashAlg; |
| |
| if (m) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| } |
| |
| if (!pool) { |
| SECU_Indent(out, level); |
| fprintf(out, "Out of memory\n"); |
| return; |
| } |
| |
| PORT_Memset(¶m, 0, sizeof param); |
| |
| rv = SEC_QuickDERDecodeItem(pool, ¶m, |
| SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate), |
| value); |
| if (rv == SECSuccess) { |
| if (!param.hashAlg) { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "Hash algorithm: default, SHA-1\n"); |
| } else { |
| SECU_PrintObjectID(out, ¶m.hashAlg->algorithm, |
| "Hash algorithm", level + 1); |
| } |
| if (!param.maskAlg) { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "Mask algorithm: default, MGF1\n"); |
| SECU_Indent(out, level + 1); |
| fprintf(out, "Mask hash algorithm: default, SHA-1\n"); |
| } else { |
| SECU_PrintObjectID(out, ¶m.maskAlg->algorithm, |
| "Mask algorithm", level + 1); |
| rv = SEC_QuickDERDecodeItem(pool, &maskHashAlg, |
| SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), |
| ¶m.maskAlg->parameters); |
| if (rv == SECSuccess) { |
| SECU_PrintObjectID(out, &maskHashAlg.algorithm, |
| "Mask hash algorithm", level + 1); |
| } else { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "Invalid mask generation algorithm parameters\n"); |
| } |
| } |
| if (!param.saltLength.data) { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "Salt length: default, %i (0x%2X)\n", 20, 20); |
| } else { |
| SECU_PrintInteger(out, ¶m.saltLength, "Salt length", level + 1); |
| } |
| } else { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "Invalid RSA-PSS parameters\n"); |
| } |
| PORT_FreeArena(pool, PR_FALSE); |
| } |
| |
| void |
| secu_PrintKDF2Params(FILE *out, SECItem *value, char *m, int level) |
| { |
| PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| SECStatus rv; |
| secuPBEParams param; |
| |
| if (m) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| } |
| |
| if (!pool) { |
| SECU_Indent(out, level); |
| fprintf(out, "Out of memory\n"); |
| return; |
| } |
| |
| PORT_Memset(¶m, 0, sizeof param); |
| rv = SEC_QuickDERDecodeItem(pool, ¶m, secuKDF2Params, value); |
| if (rv == SECSuccess) { |
| SECU_PrintAsHex(out, ¶m.salt, "Salt", level + 1); |
| SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count", |
| level + 1); |
| SECU_PrintInteger(out, ¶m.keyLength, "Key Length", level + 1); |
| SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF algorithm", level + 1); |
| } |
| PORT_FreeArena(pool, PR_FALSE); |
| } |
| |
| void |
| secu_PrintPKCS5V2Params(FILE *out, SECItem *value, char *m, int level) |
| { |
| PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| SECStatus rv; |
| secuPBEParams param; |
| |
| if (m) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| } |
| |
| if (!pool) { |
| SECU_Indent(out, level); |
| fprintf(out, "Out of memory\n"); |
| return; |
| } |
| |
| PORT_Memset(¶m, 0, sizeof param); |
| rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEV2Params, value); |
| if (rv == SECSuccess) { |
| SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF", level + 1); |
| SECU_PrintAlgorithmID(out, ¶m.cipherAlg, "Cipher", level + 1); |
| } |
| PORT_FreeArena(pool, PR_FALSE); |
| } |
| |
| void |
| secu_PrintPBEParams(FILE *out, SECItem *value, char *m, int level) |
| { |
| PLArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| SECStatus rv; |
| secuPBEParams param; |
| |
| if (m) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| } |
| |
| if (!pool) { |
| SECU_Indent(out, level); |
| fprintf(out, "Out of memory\n"); |
| return; |
| } |
| |
| PORT_Memset(¶m, 0, sizeof(secuPBEParams)); |
| rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEParamsTemp, value); |
| if (rv == SECSuccess) { |
| SECU_PrintAsHex(out, ¶m.salt, "Salt", level + 1); |
| SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count", |
| level + 1); |
| } |
| PORT_FreeArena(pool, PR_FALSE); |
| } |
| |
| /* This function does NOT expect a DER type and length. */ |
| void |
| SECU_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m, int level) |
| { |
| SECOidTag algtag; |
| SECU_PrintObjectID(out, &a->algorithm, m, level); |
| |
| algtag = SECOID_GetAlgorithmTag(a); |
| if (SEC_PKCS5IsAlgorithmPBEAlgTag(algtag)) { |
| switch (algtag) { |
| case SEC_OID_PKCS5_PBKDF2: |
| secu_PrintKDF2Params(out, &a->parameters, "Parameters", level + 1); |
| break; |
| case SEC_OID_PKCS5_PBES2: |
| secu_PrintPKCS5V2Params(out, &a->parameters, "Encryption", level + 1); |
| break; |
| case SEC_OID_PKCS5_PBMAC1: |
| secu_PrintPKCS5V2Params(out, &a->parameters, "MAC", level + 1); |
| break; |
| default: |
| secu_PrintPBEParams(out, &a->parameters, "Parameters", level + 1); |
| break; |
| } |
| return; |
| } |
| |
| if (a->parameters.len == 0 || |
| (a->parameters.len == 2 && |
| PORT_Memcmp(a->parameters.data, "\005\000", 2) == 0)) { |
| /* No arguments or NULL argument */ |
| } else if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { |
| secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1); |
| } else { |
| /* Print args to algorithm */ |
| SECU_PrintAsHex(out, &a->parameters, "Args", level + 1); |
| } |
| } |
| |
| static void |
| secu_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m, int level) |
| { |
| SECItem *value; |
| int i; |
| char om[100]; |
| |
| if (m) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| } |
| |
| /* |
| * Should make this smarter; look at the type field and then decode |
| * and print the value(s) appropriately! |
| */ |
| SECU_PrintObjectID(out, &(attr->type), "Type", level + 1); |
| if (attr->values != NULL) { |
| i = 0; |
| while ((value = attr->values[i++]) != NULL) { |
| 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; |
| curveOID.len = PR_MIN(curveOID.len, pk->u.ec.DEREncodedParams.len - 2); |
| SECU_PrintObjectID(out, &curveOID, "Curve", level + 1); |
| } |
| } |
| |
| void |
| SECU_PrintRSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level) |
| { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| SECU_PrintInteger(out, &pk->u.rsa.modulus, "Modulus", level + 1); |
| SECU_PrintInteger(out, &pk->u.rsa.publicExponent, "Exponent", level + 1); |
| if (pk->u.rsa.publicExponent.len == 1 && |
| pk->u.rsa.publicExponent.data[0] == 1) { |
| SECU_Indent(out, level + 1); |
| fprintf(out, "Error: INVALID RSA KEY!\n"); |
| } |
| } |
| |
| void |
| SECU_PrintDSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level) |
| { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| SECU_PrintInteger(out, &pk->u.dsa.params.prime, "Prime", level + 1); |
| SECU_PrintInteger(out, &pk->u.dsa.params.subPrime, "Subprime", level + 1); |
| SECU_PrintInteger(out, &pk->u.dsa.params.base, "Base", level + 1); |
| SECU_PrintInteger(out, &pk->u.dsa.publicValue, "PublicValue", level + 1); |
| } |
| |
| static void |
| secu_PrintSubjectPublicKeyInfo(FILE *out, PLArenaPool *arena, |
| CERTSubjectPublicKeyInfo *i, char *msg, int level) |
| { |
| SECKEYPublicKey *pk; |
| |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", msg); |
| SECU_PrintAlgorithmID(out, &i->algorithm, "Public Key Algorithm", level + 1); |
| |
| pk = SECKEY_ExtractPublicKey(i); |
| if (pk) { |
| switch (pk->keyType) { |
| case rsaKey: |
| SECU_PrintRSAPublicKey(out, pk, "RSA Public Key", level + 1); |
| break; |
| |
| case dsaKey: |
| SECU_PrintDSAPublicKey(out, pk, "DSA Public Key", level + 1); |
| break; |
| |
| case ecKey: |
| secu_PrintECPublicKey(out, pk, "EC Public Key", level + 1); |
| break; |
| |
| case dhKey: |
| case fortezzaKey: |
| case keaKey: |
| SECU_Indent(out, level); |
| fprintf(out, "unable to format this SPKI algorithm type\n"); |
| goto loser; |
| default: |
| SECU_Indent(out, level); |
| fprintf(out, "unknown SPKI algorithm type\n"); |
| goto loser; |
| } |
| PORT_FreeArena(pk->arena, PR_FALSE); |
| } else { |
| SECU_PrintErrMsg(out, level, "Error", "Parsing public key"); |
| loser: |
| if (i->subjectPublicKey.data) { |
| SECU_PrintAny(out, &i->subjectPublicKey, "Raw", level); |
| } |
| } |
| } |
| |
| static void |
| printStringWithoutCRLF(FILE *out, const char *str) |
| { |
| const char *c = str; |
| while (*c) { |
| if (*c != '\r' && *c != '\n') { |
| fputc(*c, out); |
| } |
| ++c; |
| } |
| } |
| |
| int |
| SECU_PrintDumpDerIssuerAndSerial(FILE *out, SECItem *der, char *m, |
| int level) |
| { |
| PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| CERTCertificate *c; |
| int rv = SEC_ERROR_NO_MEMORY; |
| char *derIssuerB64; |
| char *derSerialB64; |
| |
| if (!arena) |
| return rv; |
| |
| /* Decode certificate */ |
| c = PORT_ArenaZNew(arena, CERTCertificate); |
| if (!c) |
| goto loser; |
| c->arena = arena; |
| rv = SEC_ASN1DecodeItem(arena, c, |
| SEC_ASN1_GET(CERT_CertificateTemplate), der); |
| if (rv) { |
| SECU_PrintErrMsg(out, 0, "Error", "Parsing extension"); |
| goto loser; |
| } |
| |
| SECU_PrintName(out, &c->subject, "Subject", 0); |
| if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/ |
| SECU_Newline(out); |
| SECU_PrintName(out, &c->issuer, "Issuer", 0); |
| if (!SECU_GetWrapEnabled()) /*SECU_PrintName didn't add newline*/ |
| SECU_Newline(out); |
| SECU_PrintInteger(out, &c->serialNumber, "Serial Number", 0); |
| |
| derIssuerB64 = BTOA_ConvertItemToAscii(&c->derIssuer); |
| derSerialB64 = BTOA_ConvertItemToAscii(&c->serialNumber); |
| |
| fprintf(out, "Issuer DER Base64:\n"); |
| if (SECU_GetWrapEnabled()) { |
| fprintf(out, "%s\n", derIssuerB64); |
| } else { |
| printStringWithoutCRLF(out, derIssuerB64); |
| fputs("\n", out); |
| } |
| |
| fprintf(out, "Serial DER Base64:\n"); |
| if (SECU_GetWrapEnabled()) { |
| fprintf(out, "%s\n", derSerialB64); |
| } else { |
| printStringWithoutCRLF(out, derSerialB64); |
| fputs("\n", out); |
| } |
| |
| PORT_Free(derIssuerB64); |
| PORT_Free(derSerialB64); |
| |
| fprintf(out, "Serial DER as C source: \n{ %d, \"", c->serialNumber.len); |
| |
| { |
| unsigned int i; |
| for (i = 0; i < c->serialNumber.len; ++i) { |
| unsigned char *chardata = (unsigned char *)(c->serialNumber.data); |
| unsigned char ch = *(chardata + i); |
| |
| fprintf(out, "\\x%02x", ch); |
| } |
| fprintf(out, "\" }\n"); |
| } |
| |
| loser: |
| PORT_FreeArena(arena, PR_FALSE); |
| return rv; |
| } |
| |
| static SECStatus |
| secu_PrintX509InvalidDate(FILE *out, SECItem *value, char *msg, int level) |
| { |
| SECItem decodedValue; |
| SECStatus rv; |
| PRTime invalidTime; |
| char *formattedTime = NULL; |
| |
| decodedValue.data = NULL; |
| rv = SEC_ASN1DecodeItem(NULL, &decodedValue, |
| SEC_ASN1_GET(SEC_GeneralizedTimeTemplate), |
| value); |
| if (rv == SECSuccess) { |
| rv = DER_GeneralizedTimeToTime(&invalidTime, &decodedValue); |
| if (rv == SECSuccess) { |
| formattedTime = CERT_GenTime2FormattedAscii(invalidTime, "%a %b %d %H:%M:%S %Y"); |
| SECU_Indent(out, level + 1); |
| fprintf(out, "%s: %s\n", msg, formattedTime); |
| PORT_Free(formattedTime); |
| } |
| } |
| PORT_Free(decodedValue.data); |
| return (rv); |
| } |
| |
| static SECStatus |
| PrintExtKeyUsageExtension(FILE *out, SECItem *value, char *msg, int level) |
| { |
| 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; |
| } |
| |
| /* sometimes a PRErrorCode, other times a SECStatus. Sigh. */ |
| int |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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 */ |
| typedef enum { |
| secuPKCS7Unknown = 0, |
| secuPKCS7PKCS12AuthSafe, |
| secuPKCS7PKCS12Safe |
| } secuPKCS7State; |
| |
| static int |
| secu_PrintPKCS7ContentInfo(FILE *, SEC_PKCS7ContentInfo *, secuPKCS7State, |
| const char *, int); |
| static int |
| secu_PrintDERPKCS7ContentInfo(FILE *, SECItem *, secuPKCS7State, |
| const char *, int); |
| |
| /* |
| ** secu_PrintPKCS7EncContent |
| ** Prints a SEC_PKCS7EncryptedContentInfo (without decrypting it) |
| */ |
| static int |
| secu_PrintPKCS7EncContent(FILE *out, SEC_PKCS7EncryptedContentInfo *src, |
| secuPKCS7State state, const 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); |
| return 0; |
| } |
| |
| /* |
| ** secu_PrintRecipientInfo |
| ** Prints a PKCS7RecipientInfo type |
| */ |
| static void |
| secu_PrintRecipientInfo(FILE *out, SEC_PKCS7RecipientInfo *info, |
| const 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, |
| const 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, |
| secuPKCS7State state, 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), |
| state, "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 int |
| secu_PrintPKCS7Enveloped(FILE *out, SEC_PKCS7EnvelopedData *src, |
| secuPKCS7State state, 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); |
| } |
| } |
| |
| return secu_PrintPKCS7EncContent(out, &src->encContentInfo, state, |
| "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, |
| secuPKCS7State state, 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); |
| } |
| } |
| |
| rv = secu_PrintPKCS7EncContent(out, &src->encContentInfo, state, |
| "Encrypted Content Information", level + 1); |
| if (rv) |
| 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; |
| } |
| |
| 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 int |
| secu_PrintPKCS7Encrypted(FILE *out, SEC_PKCS7EncryptedData *src, |
| secuPKCS7State state, const char *m, int level) |
| { |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| SECU_PrintInteger(out, &(src->version), "Version", level + 1); |
| |
| return secu_PrintPKCS7EncContent(out, &src->encContentInfo, state, |
| "Encrypted Content Information", level + 1); |
| } |
| |
| /* |
| ** secu_PrintPKCS7Digested |
| ** Pretty print a PKCS7 digested data type (up to version 1). |
| */ |
| static int |
| secu_PrintPKCS7Digested(FILE *out, SEC_PKCS7DigestedData *src, |
| secuPKCS7State state, 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, state, |
| "Content Information", level + 1); |
| SECU_PrintAsHex(out, &src->digest, "Digest", level + 1); |
| return 0; |
| } |
| |
| static int |
| secu_PrintPKCS12Attributes(FILE *out, SECItem *item, const char *m, int level) |
| { |
| SECItem my = *item; |
| SECItem attribute; |
| SECItem attributeID; |
| SECItem attributeValues; |
| |
| if ((my.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SET)) || |
| SECSuccess != SECU_StripTagAndLength(&my)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| level++; |
| |
| while (my.len) { |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &attribute)) { |
| return SECFailure; |
| } |
| if ((attribute.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) || |
| SECSuccess != SECU_StripTagAndLength(&attribute)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| |
| /* attribute ID */ |
| if (SECSuccess != SECU_ExtractBERAndStep(&attribute, &attributeID)) { |
| return SECFailure; |
| } |
| if ((attributeID.data[0] & SEC_ASN1_TAGNUM_MASK) != SEC_ASN1_OBJECT_ID) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| SECU_PrintEncodedObjectID(out, &attributeID, "Attribute ID", level); |
| |
| /* attribute values */ |
| if (!attribute.len) { /* skip if there aren't any */ |
| continue; |
| } |
| if (SECSuccess != SECU_ExtractBERAndStep(&attribute, &attributeValues)) { |
| return SECFailure; |
| } |
| if (SECSuccess != SECU_StripTagAndLength(&attributeValues)) { |
| return SECFailure; |
| } |
| while (attributeValues.len) { |
| SECItem tmp; |
| if (SECSuccess != SECU_ExtractBERAndStep(&attributeValues, &tmp)) { |
| return SECFailure; |
| } |
| SECU_PrintAny(out, &tmp, NULL, level + 1); |
| } |
| } |
| return SECSuccess; |
| } |
| |
| static int |
| secu_PrintPKCS12Bag(FILE *out, SECItem *item, const char *desc, int level) |
| { |
| SECItem my = *item; |
| SECItem bagID; |
| SECItem bagValue; |
| SECItem bagAttributes; |
| SECOidTag bagTag; |
| SECStatus rv; |
| int i; |
| char *m; |
| |
| if ((my.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) || |
| SECSuccess != SECU_StripTagAndLength(&my)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| |
| /* bagId BAG-TYPE.&id ({PKCS12BagSet}) */ |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &bagID)) { |
| return SECFailure; |
| } |
| if ((bagID.data[0] & SEC_ASN1_TAGNUM_MASK) != SEC_ASN1_OBJECT_ID) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| m = PR_smprintf("%s ID", desc); |
| bagTag = SECU_PrintEncodedObjectID(out, &bagID, m ? m : "Bag ID", level); |
| if (m) |
| PR_smprintf_free(m); |
| |
| /* bagValue [0] EXPLICIT BAG-TYPE.&type({PKCS12BagSet}{@bagID}) */ |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &bagValue)) { |
| return SECFailure; |
| } |
| if ((bagValue.data[0] & (SEC_ASN1_CLASS_MASK | SEC_ASN1_TAGNUM_MASK)) != |
| (SEC_ASN1_CONTEXT_SPECIFIC | 0)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| if (SECSuccess != SECU_StripTagAndLength(&bagValue)) { |
| return SECFailure; |
| } |
| |
| rv = SECSuccess; |
| switch (bagTag) { |
| case SEC_OID_PKCS12_V1_KEY_BAG_ID: |
| /* Future we need to print out raw private keys. Not a priority since |
| * p12util can't create files with unencrypted private keys, but |
| * some tools can and do */ |
| SECU_PrintAny(out, &bagValue, "Private Key", level); |
| break; |
| case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: |
| rv = SECU_PrintPrivateKey(out, &bagValue, |
| "Encrypted Private Key", level); |
| break; |
| case SEC_OID_PKCS12_V1_CERT_BAG_ID: |
| rv = secu_PrintPKCS12Bag(out, &bagValue, "Certificate Bag", level + 1); |
| break; |
| case SEC_OID_PKCS12_V1_CRL_BAG_ID: |
| rv = secu_PrintPKCS12Bag(out, &bagValue, "Crl Bag", level + 1); |
| break; |
| case SEC_OID_PKCS12_V1_SECRET_BAG_ID: |
| rv = secu_PrintPKCS12Bag(out, &bagValue, "Secret Bag", level + 1); |
| break; |
| /* from recursive call from CRL and certificate Bag */ |
| case SEC_OID_PKCS9_X509_CRL: |
| case SEC_OID_PKCS9_X509_CERT: |
| case SEC_OID_PKCS9_SDSI_CERT: |
| /* unwrap the octect string */ |
| rv = SECU_StripTagAndLength(&bagValue); |
| if (rv != SECSuccess) { |
| break; |
| } |
| /* fall through */ |
| case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID: |
| case SEC_OID_PKCS12_X509_CERT_CRL_BAG: |
| case SEC_OID_PKCS12_SDSI_CERT_BAG: |
| if (strcmp(desc, "Crl Bag") == 0) { |
| rv = SECU_PrintSignedData(out, &bagValue, NULL, level + 1, |
| (SECU_PPFunc)SECU_PrintCrl); |
| } else { |
| rv = SECU_PrintSignedData(out, &bagValue, NULL, level + 1, |
| (SECU_PPFunc)SECU_PrintCertificate); |
| } |
| break; |
| case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: |
| for (i = 1; my.len; i++) { |
| SECItem nextBag; |
| rv = SECU_ExtractBERAndStep(&bagValue, &nextBag); |
| if (rv != SECSuccess) { |
| break; |
| } |
| m = PR_smprintf("Nested Bag %d", i); |
| rv = secu_PrintPKCS12Bag(out, &nextBag, |
| m ? m : "Nested Bag", level + 1); |
| if (m) |
| PR_smprintf_free(m); |
| if (rv != SECSuccess) { |
| break; |
| } |
| } |
| break; |
| default: |
| m = PR_smprintf("%s Value", desc); |
| SECU_PrintAny(out, &bagValue, m ? m : "Bag Value", level); |
| if (m) |
| PR_smprintf_free(m); |
| } |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| |
| /* bagAttributes SET OF PKCS12Attributes OPTIONAL */ |
| if (my.len && |
| (my.data[0] == (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SET))) { |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &bagAttributes)) { |
| return SECFailure; |
| } |
| m = PR_smprintf("%s Attributes", desc); |
| rv = secu_PrintPKCS12Attributes(out, &bagAttributes, |
| m ? m : "Bag Attributes", level); |
| if (m) |
| PR_smprintf_free(m); |
| } |
| return rv; |
| } |
| |
| static int |
| secu_PrintPKCS7Data(FILE *out, SECItem *item, secuPKCS7State state, |
| const char *desc, int level) |
| { |
| SECItem my = *item; |
| SECItem nextbag; |
| int i; |
| SECStatus rv; |
| |
| /* walk down each safe */ |
| switch (state) { |
| case secuPKCS7PKCS12AuthSafe: |
| if ((my.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) || |
| SECSuccess != SECU_StripTagAndLength(&my)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| for (i = 1; my.len; i++) { |
| char *m; |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &nextbag)) { |
| return SECFailure; |
| } |
| m = PR_smprintf("Safe %d", i); |
| rv = secu_PrintDERPKCS7ContentInfo(out, &nextbag, |
| secuPKCS7PKCS12Safe, |
| m ? m : "Safe", level); |
| if (m) |
| PR_smprintf_free(m); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| } |
| return SECSuccess; |
| case secuPKCS7PKCS12Safe: |
| if ((my.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) || |
| SECSuccess != SECU_StripTagAndLength(&my)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| for (i = 1; my.len; i++) { |
| char *m; |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &nextbag)) { |
| return SECFailure; |
| } |
| m = PR_smprintf("Bag %d", i); |
| rv = secu_PrintPKCS12Bag(out, &nextbag, |
| m ? m : "Bag", level); |
| if (m) |
| PR_smprintf_free(m); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| } |
| return SECSuccess; |
| case secuPKCS7Unknown: |
| SECU_PrintAsHex(out, item, desc, level); |
| break; |
| } |
| return SECSuccess; |
| } |
| |
| /* |
| ** secu_PrintPKCS7ContentInfo |
| ** Takes a SEC_PKCS7ContentInfo type and sends the contents to the |
| ** appropriate function |
| */ |
| static int |
| secu_PrintPKCS7ContentInfo(FILE *out, SEC_PKCS7ContentInfo *src, |
| secuPKCS7State state, const 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_UNKNOWN; |
| } 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, |
| state, desc, level); |
| break; |
| |
| case SEC_OID_PKCS7_ENVELOPED_DATA: /* Enveloped Data */ |
| rv = secu_PrintPKCS7Enveloped(out, src->content.envelopedData, |
| state, desc, level); |
| break; |
| |
| case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: /* Signed and Enveloped */ |
| rv = secu_PrintPKCS7SignedAndEnveloped(out, |
| src->content.signedAndEnvelopedData, |
| state, desc, level); |
| break; |
| |
| case SEC_OID_PKCS7_DIGESTED_DATA: /* Digested Data */ |
| rv = secu_PrintPKCS7Digested(out, src->content.digestedData, |
| state, desc, level); |
| break; |
| |
| case SEC_OID_PKCS7_ENCRYPTED_DATA: /* Encrypted Data */ |
| rv = secu_PrintPKCS7Encrypted(out, src->content.encryptedData, |
| state, desc, level); |
| break; |
| |
| case SEC_OID_PKCS7_DATA: |
| rv = secu_PrintPKCS7Data(out, src->content.data, state, 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). |
| */ |
| static int |
| secu_PrintDERPKCS7ContentInfo(FILE *out, SECItem *der, secuPKCS7State state, |
| const 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, state, m, level); |
| SEC_PKCS7DestroyContentInfo(cinfo); |
| } else { |
| rv = -1; |
| } |
| |
| return rv; |
| } |
| |
| int |
| SECU_PrintPKCS7ContentInfo(FILE *out, SECItem *der, char *m, int level) |
| { |
| return secu_PrintDERPKCS7ContentInfo(out, der, secuPKCS7Unknown, m, level); |
| } |
| |
| /* |
| ** 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); |
| } |
| |
| /* The distrust fields are hard-coded in nssckbi and read-only. |
| * If verifying some cert, with vfychain, for instance, the certificate may |
| * not have a defined slot if not imported. */ |
| if (cert->slot != NULL && cert->distrust != NULL) { |
| const unsigned int kDistrustFieldSize = 13; |
| fprintf(stdout, "\n"); |
| SECU_Indent(stdout, 1); |
| fprintf(stdout, "%s:\n", "Certificate Distrust Dates"); |
| if (cert->distrust->serverDistrustAfter.len == kDistrustFieldSize) { |
| SECU_PrintTimeChoice(stdout, |
| &cert->distrust->serverDistrustAfter, |
| "Server Distrust After", 2); |
| } |
| if (cert->distrust->emailDistrustAfter.len == kDistrustFieldSize) { |
| SECU_PrintTimeChoice(stdout, |
| &cert->distrust->emailDistrustAfter, |
| "E-mail Distrust After", 2); |
| } |
| } |
| |
| 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; |
| } |
| |
| static 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; |
| } |
| |
| static SECStatus |
| countItems(const char *arg, unsigned int *numItems) |
| { |
| char *str = PORT_Strdup(arg); |
| if (!str) { |
| return SECFailure; |
| } |
| char *p = strtok(str, ","); |
| while (p) { |
| ++(*numItems); |
| p = strtok(NULL, ","); |
| } |
| PORT_Free(str); |
| str = NULL; |
| return SECSuccess; |
| } |
| |
| SECStatus |
| parseGroupList(const char *arg, SSLNamedGroup **enabledGroups, |
| unsigned int *enabledGroupsCount) |
| { |
| SSLNamedGroup *groups; |
| char *str; |
| char *p; |
| unsigned int numValues = 0; |
| unsigned int count = 0; |
| |
| if (countItems(arg, &numValues) != SECSuccess) { |
| return SECFailure; |
| } |
| groups = PORT_ZNewArray(SSLNamedGroup, numValues); |
| if (!groups) { |
| return SECFailure; |
| } |
| |
| /* 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: |
| PORT_Free(str); |
| if (!count) { |
| PORT_Free(groups); |
| return SECFailure; |
| } |
| |
| *enabledGroupsCount = count; |
| *enabledGroups = groups; |
| return SECSuccess; |
| } |
| |
| SSLSignatureScheme |
| schemeNameToScheme(const char *name) |
| { |
| #define compareScheme(x) \ |
| do { \ |
| if (!PORT_Strncmp(name, #x, PORT_Strlen(#x))) { \ |
| return ssl_sig_##x; \ |
| } \ |
| } while (0) |
| |
| compareScheme(rsa_pkcs1_sha1); |
| compareScheme(rsa_pkcs1_sha256); |
| compareScheme(rsa_pkcs1_sha384); |
| compareScheme(rsa_pkcs1_sha512); |
| compareScheme(ecdsa_sha1); |
| compareScheme(ecdsa_secp256r1_sha256); |
| compareScheme(ecdsa_secp384r1_sha384); |
| compareScheme(ecdsa_secp521r1_sha512); |
| compareScheme(rsa_pss_rsae_sha256); |
| compareScheme(rsa_pss_rsae_sha384); |
| compareScheme(rsa_pss_rsae_sha512); |
| compareScheme(ed25519); |
| compareScheme(ed448); |
| compareScheme(rsa_pss_pss_sha256); |
| compareScheme(rsa_pss_pss_sha384); |
| compareScheme(rsa_pss_pss_sha512); |
| compareScheme(dsa_sha1); |
| compareScheme(dsa_sha256); |
| compareScheme(dsa_sha384); |
| compareScheme(dsa_sha512); |
| |
| #undef compareScheme |
| |
| return ssl_sig_none; |
| } |
| |
| SECStatus |
| parseSigSchemeList(const char *arg, const SSLSignatureScheme **enabledSigSchemes, |
| unsigned int *enabledSigSchemeCount) |
| { |
| SSLSignatureScheme *schemes; |
| unsigned int numValues = 0; |
| unsigned int count = 0; |
| |
| if (countItems(arg, &numValues) != SECSuccess) { |
| return SECFailure; |
| } |
| schemes = PORT_ZNewArray(SSLSignatureScheme, numValues); |
| if (!schemes) { |
| return SECFailure; |
| } |
| |
| /* Get group names. */ |
| char *str = PORT_Strdup(arg); |
| if (!str) { |
| goto done; |
| } |
| char *p = strtok(str, ","); |
| while (p) { |
| SSLSignatureScheme scheme = schemeNameToScheme(p); |
| if (scheme == ssl_sig_none) { |
| count = 0; |
| goto done; |
| } |
| schemes[count++] = scheme; |
| p = strtok(NULL, ","); |
| } |
| |
| done: |
| PORT_Free(str); |
| if (!count) { |
| PORT_Free(schemes); |
| return SECFailure; |
| } |
| |
| *enabledSigSchemeCount = count; |
| *enabledSigSchemes = schemes; |
| return SECSuccess; |
| } |
| |
| /* Parse the exporter spec in the form: LABEL[:OUTPUT-LENGTH[:CONTEXT]] */ |
| static SECStatus |
| parseExporter(const char *arg, |
| secuExporter *exporter) |
| { |
| SECStatus rv = SECSuccess; |
| |
| char *str = PORT_Strdup(arg); |
| if (!str) { |
| rv = SECFailure; |
| goto done; |
| } |
| |
| char *labelEnd = strchr(str, ':'); |
| if (labelEnd) { |
| *labelEnd = '\0'; |
| labelEnd++; |
| |
| /* To extract CONTEXT, first skip OUTPUT-LENGTH */ |
| char *outputEnd = strchr(labelEnd, ':'); |
| if (outputEnd) { |
| *outputEnd = '\0'; |
| outputEnd++; |
| |
| exporter->hasContext = PR_TRUE; |
| exporter->context.data = (unsigned char *)PORT_Strdup(outputEnd); |
| exporter->context.len = strlen(outputEnd); |
| if (PORT_Strncasecmp((char *)exporter->context.data, "0x", 2) == 0) { |
| rv = SECU_SECItemHexStringToBinary(&exporter->context); |
| if (rv != SECSuccess) { |
| goto done; |
| } |
| } |
| } |
| } |
| |
| if (labelEnd && *labelEnd != '\0') { |
| long int outputLength = strtol(labelEnd, NULL, 10); |
| if (!(outputLength > 0 && outputLength <= UINT_MAX)) { |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| rv = SECFailure; |
| goto done; |
| } |
| exporter->outputLength = outputLength; |
| } else { |
| exporter->outputLength = 20; |
| } |
| |
| char *label = PORT_Strdup(str); |
| exporter->label.data = (unsigned char *)label; |
| exporter->label.len = strlen(label); |
| if (PORT_Strncasecmp((char *)exporter->label.data, "0x", 2) == 0) { |
| rv = SECU_SECItemHexStringToBinary(&exporter->label); |
| if (rv != SECSuccess) { |
| goto done; |
| } |
| } |
| |
| done: |
| PORT_Free(str); |
| |
| return rv; |
| } |
| |
| SECStatus |
| parseExporters(const char *arg, |
| const secuExporter **enabledExporters, |
| unsigned int *enabledExporterCount) |
| { |
| secuExporter *exporters; |
| unsigned int numValues = 0; |
| unsigned int count = 0; |
| |
| if (countItems(arg, &numValues) != SECSuccess) { |
| return SECFailure; |
| } |
| exporters = PORT_ZNewArray(secuExporter, numValues); |
| if (!exporters) { |
| return SECFailure; |
| } |
| |
| /* Get exporter definitions. */ |
| char *str = PORT_Strdup(arg); |
| if (!str) { |
| goto done; |
| } |
| char *p = strtok(str, ","); |
| while (p) { |
| SECStatus rv = parseExporter(p, &exporters[count++]); |
| if (rv != SECSuccess) { |
| count = 0; |
| goto done; |
| } |
| p = strtok(NULL, ","); |
| } |
| |
| done: |
| PORT_Free(str); |
| if (!count) { |
| PORT_Free(exporters); |
| return SECFailure; |
| } |
| |
| *enabledExporterCount = count; |
| *enabledExporters = exporters; |
| return SECSuccess; |
| } |
| |
| static SECStatus |
| exportKeyingMaterial(PRFileDesc *fd, const secuExporter *exporter) |
| { |
| SECStatus rv = SECSuccess; |
| unsigned char *out = PORT_Alloc(exporter->outputLength); |
| |
| if (!out) { |
| fprintf(stderr, "Unable to allocate buffer for keying material\n"); |
| return SECFailure; |
| } |
| rv = SSL_ExportKeyingMaterial(fd, |
| (char *)exporter->label.data, |
| exporter->label.len, |
| exporter->hasContext, |
| exporter->context.data, |
| exporter->context.len, |
| out, |
| exporter->outputLength); |
| if (rv != SECSuccess) { |
| goto done; |
| } |
| fprintf(stdout, "Exported Keying Material:\n"); |
| secu_PrintRawString(stdout, (SECItem *)&exporter->label, "Label", 1); |
| if (exporter->hasContext) { |
| SECU_PrintAsHex(stdout, &exporter->context, "Context", 1); |
| } |
| SECU_Indent(stdout, 1); |
| fprintf(stdout, "Length: %u\n", exporter->outputLength); |
| SECItem temp = { siBuffer, out, exporter->outputLength }; |
| SECU_PrintAsHex(stdout, &temp, "Keying Material", 1); |
| |
| done: |
| PORT_Free(out); |
| return rv; |
| } |
| |
| SECStatus |
| exportKeyingMaterials(PRFileDesc *fd, |
| const secuExporter *exporters, |
| unsigned int exporterCount) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < exporterCount; i++) { |
| SECStatus rv = exportKeyingMaterial(fd, &exporters[i]); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| } |
| |
| return SECSuccess; |
| } |
| |
| SECStatus |
| readPSK(const char *arg, SECItem *psk, SECItem *label) |
| { |
| SECStatus rv = SECFailure; |
| char *str = PORT_Strdup(arg); |
| if (!str) { |
| goto cleanup; |
| } |
| |
| char *pskBytes = strtok(str, ":"); |
| if (!pskBytes) { |
| goto cleanup; |
| } |
| if (PORT_Strncasecmp(pskBytes, "0x", 2) != 0) { |
| goto cleanup; |
| } |
| |
| psk = SECU_HexString2SECItem(NULL, psk, &pskBytes[2]); |
| if (!psk || !psk->data || psk->len != strlen(&str[2]) / 2) { |
| goto cleanup; |
| } |
| |
| SECItem labelItem = { siBuffer, NULL, 0 }; |
| char *inLabel = strtok(NULL, ":"); |
| if (inLabel) { |
| labelItem.data = (unsigned char *)PORT_Strdup(inLabel); |
| if (!labelItem.data) { |
| goto cleanup; |
| } |
| labelItem.len = strlen(inLabel); |
| |
| if (PORT_Strncasecmp(inLabel, "0x", 2) == 0) { |
| rv = SECU_SECItemHexStringToBinary(&labelItem); |
| if (rv != SECSuccess) { |
| SECITEM_FreeItem(&labelItem, PR_FALSE); |
| goto cleanup; |
| } |
| } |
| rv = SECSuccess; |
| } else { |
| PRUint8 defaultLabel[] = { 'C', 'l', 'i', 'e', 'n', 't', '_', |
| 'i', 'd', 'e', 'n', 't', 'i', 't', 'y' }; |
| SECItem src = { siBuffer, defaultLabel, sizeof(defaultLabel) }; |
| rv = SECITEM_CopyItem(NULL, &labelItem, &src); |
| } |
| if (rv == SECSuccess) { |
| *label = labelItem; |
| } |
| |
| cleanup: |
| PORT_Free(str); |
| return rv; |
| } |
| |
| static SECStatus |
| secu_PrintPKCS12DigestInfo(FILE *out, const SECItem *t, char *m, int level) |
| { |
| SECItem my = *t; |
| SECItem rawDigestAlgID; |
| SECItem digestData; |
| SECStatus rv; |
| PLArenaPool *arena; |
| SECAlgorithmID digestAlgID; |
| char *mAlgID = NULL; |
| char *mDigest = NULL; |
| |
| /* strip the outer sequence */ |
| if ((my.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) || |
| SECSuccess != SECU_StripTagAndLength(&my)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| |
| /* get the algorithm ID */ |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &rawDigestAlgID)) { |
| return SECFailure; |
| } |
| arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| if (arena == NULL) { |
| return SECFailure; |
| } |
| #define DIGEST_ALGID_STRING "Digest Algorithm ID" |
| if (m) |
| mAlgID = PR_smprintf("%s " DIGEST_ALGID_STRING, m); |
| rv = SEC_QuickDERDecodeItem(arena, &digestAlgID, |
| SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), |
| &rawDigestAlgID); |
| if (rv == SECSuccess) { |
| SECU_PrintAlgorithmID(out, &digestAlgID, |
| mAlgID ? mAlgID : DIGEST_ALGID_STRING, level); |
| } |
| if (mAlgID) |
| PR_smprintf_free(mAlgID); |
| PORT_FreeArena(arena, PR_FALSE); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| |
| /* get the mac data */ |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &digestData)) { |
| return SECFailure; |
| } |
| if ((digestData.data[0] & SEC_ASN1_TAGNUM_MASK) != SEC_ASN1_OCTET_STRING) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| #define DIGEST_STRING "Digest" |
| if (m) |
| mDigest = PR_smprintf("%s " DIGEST_STRING, m); |
| secu_PrintOctetString(out, &digestData, |
| mDigest ? mDigest : DIGEST_STRING, level); |
| if (mDigest) |
| PR_smprintf_free(mDigest); |
| return SECSuccess; |
| } |
| |
| static SECStatus |
| secu_PrintPKCS12MacData(FILE *out, const SECItem *t, char *m, int level) |
| { |
| SECItem my = *t; |
| SECItem hash; |
| SECItem salt; |
| |
| if (m) { |
| SECU_Indent(out, level); |
| fprintf(out, "%s: \n", m); |
| level++; |
| } |
| |
| /* strip the outer sequence */ |
| if ((my.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) || |
| SECSuccess != SECU_StripTagAndLength(&my)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &hash)) { |
| return SECFailure; |
| } |
| if (SECSuccess != secu_PrintPKCS12DigestInfo(out, &hash, "Mac", level)) { |
| return SECFailure; |
| } |
| |
| /* handle the salt */ |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &salt)) { |
| return SECFailure; |
| ; |
| } |
| if ((salt.data[0] & SEC_ASN1_TAGNUM_MASK) != SEC_ASN1_OCTET_STRING) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| secu_PrintOctetString(out, &salt, "Mac Salt", level); |
| |
| if (my.len && |
| ((my.data[0] & SEC_ASN1_TAGNUM_MASK) == SEC_ASN1_INTEGER)) { |
| SECItem iterator; |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &iterator)) { |
| return SECFailure; |
| } |
| SECU_PrintEncodedInteger(out, &iterator, "Iterations", level); |
| } |
| return SECSuccess; |
| } |
| |
| SECStatus |
| SECU_PrintPKCS12(FILE *out, const SECItem *t, char *m, int level) |
| { |
| SECItem my = *t; |
| SECItem authSafe; |
| SECItem macData; |
| |
| SECU_Indent(out, level); |
| fprintf(out, "%s:\n", m); |
| level++; |
| |
| /* strip the outer sequence */ |
| if ((my.data[0] != (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) || |
| SECSuccess != SECU_StripTagAndLength(&my)) { |
| PORT_SetError(SEC_ERROR_BAD_DER); |
| return SECFailure; |
| } |
| /* print and remove the optional version number */ |
| if (my.len && ((my.data[0] & SEC_ASN1_TAGNUM_MASK) == SEC_ASN1_INTEGER)) { |
| SECItem version; |
| |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &version)) { |
| return SECFailure; |
| } |
| SECU_PrintEncodedInteger(out, &version, "Version", level); |
| } |
| |
| /* print the authSafe */ |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &authSafe)) { |
| return SECFailure; |
| } |
| if (SECSuccess != secu_PrintDERPKCS7ContentInfo(out, &authSafe, |
| secuPKCS7PKCS12AuthSafe, |
| "AuthSafe", level)) { |
| return SECFailure; |
| } |
| |
| /* print the mac data (optional) */ |
| if (!my.len) { |
| return SECSuccess; |
| } |
| if (SECSuccess != SECU_ExtractBERAndStep(&my, &macData)) { |
| return SECFailure; |
| } |
| if (SECSuccess != secu_PrintPKCS12MacData(out, &macData, |
| "Mac Data", level)) { |
| return SECFailure; |
| } |
| |
| if (my.len) { |
| fprintf(out, "Unknown extra data found \n"); |
| } |
| return SECSuccess; |
| } |