These utility functions are adapted from those found in the sectool library used by the NSS security tools and other NSS test applications.
It shows the following:
/* 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 https://mozilla.org/MPL/2.0/. */ #ifndef _UTIL_H #define _UTIL_H #include <prlog.h> #include <termios.h> #include <base64.h> #include <unistd.h> #include <sys/stat.h> #include "util.h" #include <prprf.h> #include <prerror.h> #include <nss.h> #include <pk11func.h> /* * These utility functions are adapted from those found in * the sectool library used by the NSS security tools and * other NSS test applications. */ typedef struct { enum { PW_NONE = 0, /* no password */ PW_FROMFILE = 1, /* password stored in a file */ PW_PLAINTEXT = 2 /* plain-text password passed in buffer */ /* PW_EXTERNAL = 3 */ } source; char *data; /* depending on source this can be the actual * password or the file to read it from */ } secuPWData; /* * PrintAsAscii */ extern void PrintAsAscii(PRFileDesc* out, const unsigned char *data, unsigned int len); /* * PrintAsHex */ extern void PrintAsHex(PRFileDesc* out, const unsigned char *data, unsigned int len); /* * GetDigit */ extern int GetDigit(char c); /* * HexToBuf */ extern int HexToBuf(unsigned char *inString, SECItem *outbuf, PRBool isHexData); /* * FileToItem */ extern SECStatus FileToItem(SECItem *dst, PRFileDesc *src); /* * CheckPassword */ extern PRBool CheckPassword(char *cp); /* * GetPassword */ extern char * GetPassword(FILE *input, FILE *output, char *prompt, PRBool (*ok)(char *)); /* * FilePasswd extracts the password from a text file * * Storing passwords is often used with server environments * where prompting the user for a password or requiring it * to be entered in the commnd line is not a feasible option. * * This function supports password extraction from files with * multipe passwords, one for each token. In the single password * case a line would just have the passord whereas in the multi- * password variant they could be of the form * * token_1_name:its_password * token_2_name:its_password * */ extern char * FilePasswd(PK11SlotInfo * slot, PRBool retry, void *arg); /* * GetModulePassword */ extern char * GetModulePassword(PK11SlotInfo *slot, int retry, void *pwdata); /* * GenerateRandom */ extern SECStatus GenerateRandom(unsigned char *rbuf, int rsize); /* * FileToItem */ extern SECStatus FileToItem(SECItem *dst, PRFileDesc *src); /* * SeedFromNoiseFile */ extern SECStatus SeedFromNoiseFile(const char *noiseFileName); /* * FileSize */ extern long FileSize(const char* filename); /* * ReadDERFromFile */ extern SECStatus ReadDERFromFile(SECItem *der, const char *inFileName, PRBool ascii); #endif /* _UTIL_H */
/* 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 https://mozilla.org/MPL/2.0/. */ #include "util.h" /* * These utility functions are adapted from those found in * the sectool library used by the NSS security tools and * other NSS test applications. */ /* * Newline */ static void Newline(PRFileDesc* out) { PR_fprintf(out, "\n"); } /* * PrintAsAscii */ void PrintAsAscii(PRFileDesc* out, const unsigned char *data, unsigned int len) { char *b64Data = NULL; b64Data = BTOA_DataToAscii(data, len); PR_fprintf(out, "%s", b64Data); PR_fprintf(out, "\n"); if (b64Data) { PORT_Free(b64Data); } } /* * PrintAsHex */ void PrintAsHex(PRFileDesc* out, const unsigned char *data, unsigned int len) { unsigned i; int column; unsigned int limit = 15; unsigned int level = 1; column = level; if (!len) { PR_fprintf(out, "(empty)\n"); return; } for (i = 0; i < len; i++) { if (i != len - 1) { PR_fprintf(out, "%02x:", data[i]); column += 3; } else { PR_fprintf(out, "%02x", data[i]); column += 2; break; } if (column > 76 || (i % 16 == limit)) { Newline(out); column = level; limit = i % 16; } } if (column != level) { Newline(out); } } /* * GetDigit */ int GetDigit(char c) { if (c == 0) { return -1; } if (c <= '9' && c >= '0') { return c - '0'; } if (c <= 'f' && c >= 'a') { return c - 'a' + 0xa; } if (c <= 'F' && c >= 'A') { return c - 'A' + 0xa; } return -1; } /* * HexToBuf */ int HexToBuf(unsigned char *inString, SECItem *outbuf, PRBool isHexData) { int len = strlen((const char *)inString); int outLen = len+1/2; int trueLen = 0; int digit1, digit2; outbuf->data = isHexData ? PORT_Alloc(outLen) : PORT_Alloc(len); if (!outbuf->data) { return -1; } if (isHexData) { while (*inString) { if ((*inString == '\n') || (*inString == ':')) { inString++; continue; } digit1 = GetDigit(*inString++); digit2 = GetDigit(*inString++); if ((digit1 == -1) || (digit2 == -1)) { PORT_Free(outbuf->data); outbuf->data = NULL; return -1; } outbuf->data[trueLen++] = digit1 << 4 | digit2; } } else { while (*inString) { if (*inString == '\n') { inString++; continue; } outbuf->data[trueLen++] = *inString++; } outbuf->data[trueLen] = '\0'; trueLen = trueLen-1; } outbuf->len = trueLen; return 0; } /* * FileToItem */ SECStatus FileToItem(SECItem *dst, PRFileDesc *src) { PRFileInfo info; PRInt32 numBytes; PRStatus prStatus; prStatus = PR_GetOpenFileInfo(src, &info); if (prStatus != PR_SUCCESS) { return SECFailure; } dst->data = 0; if (SECITEM_AllocItem(NULL, dst, info.size)) { numBytes = PR_Read(src, dst->data, info.size); if (numBytes == info.size) { return SECSuccess; } } SECITEM_FreeItem(dst, PR_FALSE); dst->data = NULL; return SECFailure; } /* * echoOff */ static void echoOff(int fd) { if (isatty(fd)) { struct termios tio; tcgetattr(fd, &tio); tio.c_lflag &= ~ECHO; tcsetattr(fd, TCSAFLUSH, &tio); } } /* * echoOn */ static void echoOn(int fd) { if (isatty(fd)) { struct termios tio; tcgetattr(fd, &tio); tio.c_lflag |= ECHO; tcsetattr(fd, TCSAFLUSH, &tio); } } /* * CheckPassword */ PRBool CheckPassword(char *cp) { int len; char *end; len = PORT_Strlen(cp); if (len < 8) { return PR_FALSE; } end = cp + len; while (cp < end) { unsigned char ch = *cp++; if (!((ch >= 'A') && (ch <= 'Z')) && !((ch >= 'a') && (ch <= 'z'))) { return PR_TRUE; } } return PR_FALSE; } /* * GetPassword */ char* GetPassword(FILE *input, FILE *output, char *prompt, PRBool (*ok)(char *)) { char phrase[200] = {'\0'}; int infd = fileno(input); int isTTY = isatty(infd); for (;;) { /* Prompt for password */ if (isTTY) { fprintf(output, "%s", prompt); fflush (output); echoOff(infd); } fgets(phrase, sizeof(phrase), input); if (isTTY) { fprintf(output, "\n"); echoOn(infd); } /* stomp on newline */ phrase[PORT_Strlen(phrase)-1] = 0; /* Validate password */ if (!(*ok)(phrase)) { if (!isTTY) return 0; fprintf(output, "Password must be at least 8 characters long with one or more\n"); fprintf(output, "non-alphabetic characters\n"); continue; } return (char*) PORT_Strdup(phrase); } } /* * FilePasswd extracts the password from a text file * * Storing passwords is often used with server environments * where prompting the user for a password or requiring it * to be entered in the commnd line is not a feasible option. * * This function supports password extraction from files with * multipe passwords, one for each token. In the single password * case a line would just have the passord whereas in the multi- * password variant they could be of the form * * token_1_name:its_password * token_2_name:its_password * */ char * 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; } /* * GetModulePassword */ char* GetModulePassword(PK11SlotInfo *slot, int retry, void *arg) { char prompt[255]; secuPWData *pwdata = (secuPWData *)arg; char *pw; if (pwdata == NULL) { return NULL; } 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 GetPassword(stdin, stdout, prompt, CheckPassword); case PW_FROMFILE: pw = FilePasswd(slot, retry, pwdata->data); pwdata->source = PW_PLAINTEXT; pwdata->data = PL_strdup(pw); return pw; case PW_PLAINTEXT: return PL_strdup(pwdata->data); default: break; } PR_fprintf(PR_STDERR, "Password check failed: No password found.\n"); return NULL; } /* * GenerateRandom */ SECStatus GenerateRandom(unsigned char *rbuf, int rsize) { char meter[] = { "\r| |" }; int fd, count; int c; SECStatus rv = SECSuccess; cc_t orig_cc_min; cc_t orig_cc_time; tcflag_t orig_lflag; struct termios tio; fprintf(stderr, "To generate random numbers, " "continue typing until the progress meter is full:\n\n"); fprintf(stderr, "%s", meter); fprintf(stderr, "\r|"); /* turn off echo on stdin & return on 1 char instead of NL */ fd = fileno(stdin); tcgetattr(fd, &tio); orig_lflag = tio.c_lflag; orig_cc_min = tio.c_cc[VMIN]; orig_cc_time = tio.c_cc[VTIME]; tio.c_lflag &= ~ECHO; tio.c_lflag &= ~ICANON; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; tcsetattr(fd, TCSAFLUSH, &tio); /* Get random noise from keyboard strokes */ count = 0; while (count < rsize) { c = getc(stdin); if (c == EOF) { rv = SECFailure; break; } *(rbuf + count) = c; if (count == 0 || c != *(rbuf + count -1)) { count++; fprintf(stderr, "*"); } } rbuf[count] = '\0'; fprintf(stderr, "\n\nFinished. Press enter to continue: "); while ((c = getc(stdin)) != '\n' && c != EOF) ; if (c == EOF) rv = SECFailure; fprintf(stderr, "\n"); /* set back termio the way it was */ tio.c_lflag = orig_lflag; tio.c_cc[VMIN] = orig_cc_min; tio.c_cc[VTIME] = orig_cc_time; tcsetattr(fd, TCSAFLUSH, &tio); return rv; } /* * SeedFromNoiseFile */ SECStatus SeedFromNoiseFile(const char *noiseFileName) { char buf[2048]; PRFileDesc *fd; PRInt32 count; fd = PR_Open(noiseFileName, PR_RDONLY, 0); if (!fd) { fprintf(stderr, "failed to open noise file."); return SECFailure; } do { count = PR_Read(fd,buf,sizeof(buf)); if (count > 0) { PK11_RandomUpdate(buf,count); } } while (count > 0); PR_Close(fd); return SECSuccess; } /* * FileSize */ long FileSize(const char* filename) { struct stat stbuf; stat(filename, &stbuf); return stbuf.st_size; } /* * ReadDERFromFile */ SECStatus ReadDERFromFile(SECItem *der, const char *inFileName, PRBool ascii) { SECStatus rv = SECSuccess; PRFileDesc *inFile = NULL; inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Failed to open file \"%s\" (%ld, %ld).\n", inFileName, PR_GetError(), PR_GetOSError()); rv = SECFailure; goto cleanup; } if (ascii) { /* First convert ascii to binary */ SECItem filedata; char *asc, *body; /* Read in ascii data */ rv = FileToItem(&filedata, inFile); asc = (char *)filedata.data; if (!asc) { PR_fprintf(PR_STDERR, "unable to read data from input file\n"); rv = SECFailure; goto cleanup; } /* 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 { PR_fprintf(PR_STDERR, "input has header but no trailer\n"); PORT_Free(filedata.data); rv = SECFailure; goto cleanup; } } else { body = asc; } /* Convert to binary */ rv = ATOB_ConvertAsciiToItem(der, body); if (rv) { PR_fprintf(PR_STDERR, "error converting ascii to binary %s\n", PORT_GetError()); PORT_Free(filedata.data); rv = SECFailure; goto cleanup; } PORT_Free(filedata.data); } else { /* Read in binary der */ rv = FileToItem(der, inFile); if (rv) { PR_fprintf(PR_STDERR, "error converting der \n"); rv = SECFailure; } } cleanup: if (inFile) { PR_Close(inFile); } return rv; }