/* 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 "basicutil.h" #include #if !defined(_WIN32_WCE) #include #include #endif #ifdef XP_UNIX #include #endif #include "secoid.h" extern long DER_GetInteger(SECItem *src); static PRBool wrapEnabled = PR_TRUE; void SECU_EnableWrap(PRBool enable) { wrapEnabled = enable; } PRBool SECU_GetWrapEnabled() { return wrapEnabled; } void SECU_PrintErrMsg(FILE *out, int level, char *progName, char *msg, ...) { va_list args; PRErrorCode err = PORT_GetError(); const char * errString = PORT_ErrorToString(err); va_start(args, msg); SECU_Indent(out, level); fprintf(out, "%s: ", progName); vfprintf(out, msg, args); if (errString != NULL && PORT_Strlen(errString) > 0) fprintf(out, ": %s\n", errString); else fprintf(out, ": error %d\n", (int)err); va_end(args); } void SECU_PrintError(char *progName, char *msg, ...) { va_list args; PRErrorCode err = PORT_GetError(); const char * errString = PORT_ErrorToString(err); va_start(args, msg); fprintf(stderr, "%s: ", progName); vfprintf(stderr, msg, args); if (errString != NULL && PORT_Strlen(errString) > 0) fprintf(stderr, ": %s\n", errString); else fprintf(stderr, ": error %d\n", (int)err); va_end(args); } void SECU_PrintSystemError(char *progName, char *msg, ...) { va_list args; va_start(args, msg); fprintf(stderr, "%s: ", progName); vfprintf(stderr, msg, args); #if defined(_WIN32_WCE) fprintf(stderr, ": %d\n", PR_GetOSError()); #else fprintf(stderr, ": %s\n", strerror(errno)); #endif va_end(args); } SECStatus secu_StdinToItem(SECItem *dst) { unsigned char buf[1000]; PRInt32 numBytes; PRBool notDone = PR_TRUE; dst->len = 0; dst->data = NULL; while (notDone) { numBytes = PR_Read(PR_STDIN, buf, sizeof(buf)); if (numBytes < 0) { return SECFailure; } if (numBytes == 0) break; if (dst->data) { unsigned char * p = dst->data; dst->data = (unsigned char*)PORT_Realloc(p, dst->len + numBytes); if (!dst->data) { PORT_Free(p); } } else { dst->data = (unsigned char*)PORT_Alloc(numBytes); } if (!dst->data) { return SECFailure; } PORT_Memcpy(dst->data + dst->len, buf, numBytes); dst->len += numBytes; } return SECSuccess; } SECStatus SECU_FileToItem(SECItem *dst, PRFileDesc *src) { PRFileInfo info; PRInt32 numBytes; PRStatus prStatus; if (src == PR_STDIN) return secu_StdinToItem(dst); prStatus = PR_GetOpenFileInfo(src, &info); if (prStatus != PR_SUCCESS) { PORT_SetError(SEC_ERROR_IO); return SECFailure; } /* XXX workaround for 3.1, not all utils zero dst before sending */ dst->data = 0; if (!SECITEM_AllocItem(NULL, dst, info.size)) goto loser; numBytes = PR_Read(src, dst->data, info.size); if (numBytes != info.size) { PORT_SetError(SEC_ERROR_IO); goto loser; } return SECSuccess; loser: SECITEM_FreeItem(dst, PR_FALSE); dst->data = NULL; return SECFailure; } SECStatus SECU_TextFileToItem(SECItem *dst, PRFileDesc *src) { PRFileInfo info; PRInt32 numBytes; PRStatus prStatus; unsigned char *buf; if (src == PR_STDIN) return secu_StdinToItem(dst); prStatus = PR_GetOpenFileInfo(src, &info); if (prStatus != PR_SUCCESS) { PORT_SetError(SEC_ERROR_IO); return SECFailure; } buf = (unsigned char*)PORT_Alloc(info.size); if (!buf) return SECFailure; numBytes = PR_Read(src, buf, info.size); if (numBytes != info.size) { PORT_SetError(SEC_ERROR_IO); goto loser; } if (buf[numBytes-1] == '\n') numBytes--; #ifdef _WINDOWS if (buf[numBytes-1] == '\r') numBytes--; #endif /* XXX workaround for 3.1, not all utils zero dst before sending */ dst->data = 0; if (!SECITEM_AllocItem(NULL, dst, numBytes)) goto loser; memcpy(dst->data, buf, numBytes); PORT_Free(buf); return SECSuccess; loser: PORT_Free(buf); return SECFailure; } #define INDENT_MULT 4 void SECU_Indent(FILE *out, int level) { int i; for (i = 0; i < level; i++) { fprintf(out, " "); } } void SECU_Newline(FILE *out) { fprintf(out, "\n"); } void SECU_PrintAsHex(FILE *out, SECItem *data, const char *m, int level) { unsigned i; int column; PRBool isString = PR_TRUE; PRBool isWhiteSpace = PR_TRUE; PRBool printedHex = PR_FALSE; unsigned int limit = 15; if ( m ) { SECU_Indent(out, level); fprintf(out, "%s:", m); level++; if (wrapEnabled) fprintf(out, "\n"); } if (wrapEnabled) { SECU_Indent(out, level); column = level*INDENT_MULT; } if (!data->len) { fprintf(out, "(empty)\n"); return; } /* take a pass to see if it's all printable. */ for (i = 0; i < data->len; i++) { unsigned char val = data->data[i]; if (!val || !isprint(val)) { isString = PR_FALSE; break; } if (isWhiteSpace && !isspace(val)) { isWhiteSpace = PR_FALSE; } } /* Short values, such as bit strings (which are printed with this ** function) often look like strings, but we want to see the bits. ** so this test assures that short values will be printed in hex, ** perhaps in addition to being printed as strings. ** The threshold size (4 bytes) is arbitrary. */ if (!isString || data->len <= 4) { for (i = 0; i < data->len; i++) { if (i != data->len - 1) { fprintf(out, "%02x:", data->data[i]); column += 3; } else { fprintf(out, "%02x", data->data[i]); column += 2; break; } if (wrapEnabled && (column > 76 || (i % 16 == limit))) { SECU_Newline(out); SECU_Indent(out, level); column = level*INDENT_MULT; limit = i % 16; } } printedHex = PR_TRUE; } if (isString && !isWhiteSpace) { if (printedHex != PR_FALSE) { SECU_Newline(out); SECU_Indent(out, level); column = level*INDENT_MULT; } for (i = 0; i < data->len; i++) { unsigned char val = data->data[i]; if (val) { fprintf(out,"%c",val); column++; } else { column = 77; } if (wrapEnabled && column > 76) { SECU_Newline(out); SECU_Indent(out, level); column = level*INDENT_MULT; } } } if (column != level*INDENT_MULT) { SECU_Newline(out); } } const char *hex = "0123456789abcdef"; const char printable[257] = { "................" /* 0x */ "................" /* 1x */ " !\"#$%&'()*+,-./" /* 2x */ "0123456789:;<=>?" /* 3x */ "@ABCDEFGHIJKLMNO" /* 4x */ "PQRSTUVWXYZ[\\]^_" /* 5x */ "`abcdefghijklmno" /* 6x */ "pqrstuvwxyz{|}~." /* 7x */ "................" /* 8x */ "................" /* 9x */ "................" /* ax */ "................" /* bx */ "................" /* cx */ "................" /* dx */ "................" /* ex */ "................" /* fx */ }; void SECU_PrintBuf(FILE *out, const char *msg, const void *vp, int len) { const unsigned char *cp = (const unsigned char *)vp; char buf[80]; char *bp; char *ap; fprintf(out, "%s [Len: %d]\n", msg, len); memset(buf, ' ', sizeof buf); bp = buf; ap = buf + 50; while (--len >= 0) { unsigned char ch = *cp++; *bp++ = hex[(ch >> 4) & 0xf]; *bp++ = hex[ch & 0xf]; *bp++ = ' '; *ap++ = printable[ch]; if (ap - buf >= 66) { *ap = 0; fprintf(out, " %s\n", buf); memset(buf, ' ', sizeof buf); bp = buf; ap = buf + 50; } } if (bp > buf) { *ap = 0; fprintf(out, " %s\n", buf); } } /* This expents i->data[0] to be the MSB of the integer. ** if you want to print a DER-encoded integer (with the tag and length) ** call SECU_PrintEncodedInteger(); */ void SECU_PrintInteger(FILE *out, SECItem *i, char *m, int level) { int iv; if (!i || !i->len || !i->data) { SECU_Indent(out, level); if (m) { fprintf(out, "%s: (null)\n", m); } else { fprintf(out, "(null)\n"); } } else if (i->len > 4) { SECU_PrintAsHex(out, i, m, level); } else { if (i->type == siUnsignedInteger && *i->data & 0x80) { /* Make sure i->data has zero in the highest bite * if i->data is an unsigned integer */ SECItem tmpI; char data[] = {0, 0, 0, 0, 0}; PORT_Memcpy(data + 1, i->data, i->len); tmpI.len = i->len + 1; tmpI.data = (void*)data; iv = DER_GetInteger(&tmpI); } else { iv = DER_GetInteger(i); } SECU_Indent(out, level); if (m) { fprintf(out, "%s: %d (0x%x)\n", m, iv, iv); } else { fprintf(out, "%d (0x%x)\n", iv, iv); } } } #if defined(DEBUG) || defined(FORCE_PR_ASSERT) /* Returns true iff a[i].flag has a duplicate in a[i+1 : count-1] */ static PRBool HasShortDuplicate(int i, secuCommandFlag *a, int count) { char target = a[i].flag; int j; /* duplicate '\0' flags are okay, they are used with long forms */ for (j = i+1; j < count; j++) { if (a[j].flag && a[j].flag == target) { return PR_TRUE; } } return PR_FALSE; } /* Returns true iff a[i].longform has a duplicate in a[i+1 : count-1] */ static PRBool HasLongDuplicate(int i, secuCommandFlag *a, int count) { int j; char *target = a[i].longform; if (!target) return PR_FALSE; for (j = i+1; j < count; j++) { if (a[j].longform && strcmp(a[j].longform, target) == 0) { return PR_TRUE; } } return PR_FALSE; } /* Returns true iff a has no short or long form duplicates */ PRBool HasNoDuplicates(secuCommandFlag *a, int count) { int i; for (i = 0; i < count; i++) { if (a[i].flag && HasShortDuplicate(i, a, count)) { return PR_FALSE; } if (a[i].longform && HasLongDuplicate(i, a, count)) { return PR_FALSE; } } return PR_TRUE; } #endif SECStatus SECU_ParseCommandLine(int argc, char **argv, char *progName, const secuCommand *cmd) { PRBool found; PLOptState *optstate; PLOptStatus status; char *optstring; PLLongOpt *longopts = NULL; int i, j; int lcmd = 0, lopt = 0; PR_ASSERT(HasNoDuplicates(cmd->commands, cmd->numCommands)); PR_ASSERT(HasNoDuplicates(cmd->options, cmd->numOptions)); optstring = (char *)PORT_Alloc(cmd->numCommands + 2*cmd->numOptions+1); if (optstring == NULL) return SECFailure; j = 0; for (i=0; inumCommands; i++) { if (cmd->commands[i].flag) /* single character option ? */ optstring[j++] = cmd->commands[i].flag; if (cmd->commands[i].longform) lcmd++; } for (i=0; inumOptions; i++) { if (cmd->options[i].flag) { optstring[j++] = cmd->options[i].flag; if (cmd->options[i].needsArg) optstring[j++] = ':'; } if (cmd->options[i].longform) lopt++; } optstring[j] = '\0'; if (lcmd + lopt > 0) { longopts = PORT_NewArray(PLLongOpt, lcmd+lopt+1); if (!longopts) { PORT_Free(optstring); return SECFailure; } j = 0; for (i=0; jnumCommands; i++) { if (cmd->commands[i].longform) { longopts[j].longOptName = cmd->commands[i].longform; longopts[j].longOption = 0; longopts[j++].valueRequired = cmd->commands[i].needsArg; } } lopt += lcmd; for (i=0; jnumOptions; i++) { if (cmd->options[i].longform) { longopts[j].longOptName = cmd->options[i].longform; longopts[j].longOption = 0; longopts[j++].valueRequired = cmd->options[i].needsArg; } } longopts[j].longOptName = NULL; } optstate = PL_CreateLongOptState(argc, argv, optstring, longopts); if (!optstate) { PORT_Free(optstring); PORT_Free(longopts); return SECFailure; } /* Parse command line arguments */ while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { const char *optstatelong; char option = optstate->option; /* positional parameter, single-char option or long opt? */ if (optstate->longOptIndex == -1) { /* not a long opt */ if (option == '\0') continue; /* it's a positional parameter */ optstatelong = ""; } else { /* long opt */ if (option == '\0') option = '\377'; /* force unequal with all flags */ optstatelong = longopts[optstate->longOptIndex].longOptName; } found = PR_FALSE; for (i=0; inumCommands; i++) { if (cmd->commands[i].flag == option || cmd->commands[i].longform == optstatelong) { cmd->commands[i].activated = PR_TRUE; if (optstate->value) { cmd->commands[i].arg = (char *)optstate->value; } found = PR_TRUE; break; } } if (found) continue; for (i=0; inumOptions; i++) { if (cmd->options[i].flag == option || cmd->options[i].longform == optstatelong) { cmd->options[i].activated = PR_TRUE; if (optstate->value) { cmd->options[i].arg = (char *)optstate->value; } else if (cmd->options[i].needsArg) { status = PL_OPT_BAD; goto loser; } found = PR_TRUE; break; } } if (!found) { status = PL_OPT_BAD; break; } } loser: PL_DestroyOptState(optstate); PORT_Free(optstring); if (longopts) PORT_Free(longopts); if (status == PL_OPT_BAD) return SECFailure; return SECSuccess; } char * SECU_GetOptionArg(const secuCommand *cmd, int optionNum) { if (optionNum < 0 || optionNum >= cmd->numOptions) return NULL; if (cmd->options[optionNum].activated) return PL_strdup(cmd->options[optionNum].arg); else return NULL; } void SECU_PrintPRandOSError(char *progName) { char buffer[513]; PRInt32 errLen = PR_GetErrorTextLength(); if (errLen > 0 && errLen < sizeof buffer) { PR_GetErrorText(buffer); } SECU_PrintError(progName, "function failed"); if (errLen > 0 && errLen < sizeof buffer) { PR_fprintf(PR_STDERR, "\t%s\n", buffer); } } SECOidTag SECU_StringToSignatureAlgTag(const char *alg) { SECOidTag hashAlgTag = SEC_OID_UNKNOWN; if (alg) { if (!PL_strcmp(alg, "MD2")) { hashAlgTag = SEC_OID_MD2; } else if (!PL_strcmp(alg, "MD4")) { hashAlgTag = SEC_OID_MD4; } else if (!PL_strcmp(alg, "MD5")) { hashAlgTag = SEC_OID_MD5; } else if (!PL_strcmp(alg, "SHA1")) { hashAlgTag = SEC_OID_SHA1; } else if (!PL_strcmp(alg, "SHA224")) { hashAlgTag = SEC_OID_SHA224; } else if (!PL_strcmp(alg, "SHA256")) { hashAlgTag = SEC_OID_SHA256; } else if (!PL_strcmp(alg, "SHA384")) { hashAlgTag = SEC_OID_SHA384; } else if (!PL_strcmp(alg, "SHA512")) { hashAlgTag = SEC_OID_SHA512; } } return hashAlgTag; } /* Caller ensures that dst is at least item->len*2+1 bytes long */ void SECU_SECItemToHex(const SECItem * item, char * dst) { if (dst && item && item->data) { unsigned char * src = item->data; unsigned int len = item->len; for (; len > 0; --len, dst += 2) { sprintf(dst, "%02x", *src++); } *dst = '\0'; } } static unsigned char nibble(char c) { c = PORT_Tolower(c); return ( c >= '0' && c <= '9') ? c - '0' : ( c >= 'a' && c <= 'f') ? c - 'a' +10 : -1; } SECStatus SECU_SECItemHexStringToBinary(SECItem* srcdest) { int i; if (!srcdest) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (srcdest->len < 4 || (srcdest->len % 2) ) { /* too short to convert, or even number of characters */ PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } if (PORT_Strncasecmp((const char*)srcdest->data, "0x", 2)) { /* wrong prefix */ PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } /* 1st pass to check for hex characters */ for (i=2; ilen; i++) { char c = PORT_Tolower(srcdest->data[i]); if (! ( ( c >= '0' && c <= '9') || ( c >= 'a' && c <= 'f') ) ) { PORT_SetError(SEC_ERROR_BAD_DATA); return SECFailure; } } /* 2nd pass to convert */ for (i=2; ilen; i+=2) { srcdest->data[(i-2)/2] = (nibble(srcdest->data[i]) << 4) + nibble(srcdest->data[i+1]); } /* adjust length */ srcdest->len -= 2; srcdest->len /= 2; return SECSuccess; }