Initialize NSS database - sample 2

NSS sample code 2: initialize the NSS database.

The NSS sample code below demonstrates how to initialize the NSS database.

/*
 * Print a usage message and exit
 */
static void
Usage(const char *progName)
{
    fprintf(stderr, "\nUsage:  %s -d  [-p ]"
                    " [-f ]\n\n",
                    progName);
    fprintf(stderr, "%-15s Specify a DB directory path\n\n",
             "-d ");
    fprintf(stderr, "%-15s Specify a plaintext password\n\n",
             "-p ");
    fprintf(stderr, "%-15s Specify a password file\n\n",
             "-f ");
    exit(-1);
}

/*
 * InitSlotPassword
 */
char *
InitSlotPassword(PK11SlotInfo *slot, PRBool retry, void *arg)
{
   FILE       *input;
   FILE       *output;
   char       *p0            = NULL;
   char       *p1            = NULL;
   secuPWData *pwdata        = (secuPWData *) arg;

   if (pwdata->source == PW_FROMFILE) {
       return FilePasswd(slot, retry, pwdata->data);
   }
   if (pwdata->source == PW_PLAINTEXT) {
       return PL_strdup(pwdata->data);
   }

   /* open terminal */
   input = fopen("/dev/tty", "r");
   if (input == NULL) {
       PR_fprintf(PR_STDERR, "Error opening input terminal for read\n");
       return NULL;
   }

   /* we have no password, so initialize database with one */
   PR_fprintf(PR_STDERR,
       "Enter a password which will be used to encrypt your keys.\n"
       "The password should be at least 8 characters long,\n"
       "and should contain at least one non-alphabetic character.\n\n");

   output = fopen("/dev/tty", "w");
   if (output == NULL) {
       PR_fprintf(PR_STDERR, "Error opening output terminal for write\n");
       return NULL;
   }

   for (;;) {
       if (p0)
           PORT_Free(p0);
       p0 = GetPassword(input, output, "Enter new password: ",
                                                CheckPassword);
       if (p1)
           PORT_Free(p1);
       p1 = GetPassword(input, output, "Re-enter password: ",
                                                CheckPassword);
       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 */
   if (p1) {
       PORT_Memset(p1, 0, PORT_Strlen(p1));
       PORT_Free(p1);
   }
   fclose(input);
   fclose(output);

   return p0;
}

/*
 * ChangePW
 */
SECStatus
ChangePW(PK11SlotInfo *slot, char *oldPass, char *newPass,
         char *oldPwFile, char *newPwFile)
{
    SECStatus  rv;
    secuPWData pwdata;
    secuPWData newpwdata;
    char      *oldpw = NULL;
    char      *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 = NULL;
    } else {
        newpwdata.source = PW_NONE;
        newpwdata.data = NULL;
    }

    if (PK11_NeedUserInit(slot)) {
        newpw = InitSlotPassword(slot, PR_FALSE, &pwdata);
        rv = PK11_InitPin(slot, (char*)NULL, newpw);
        if (rv == SECSuccess) {
            PR_fprintf(PR_STDERR, "PK11_InitPin failed.\n");
            return SECFailure;
        }
    }
    else {
        for (;;) {
            oldpw = 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);
                    return SECFailure;
                }
            } else {
                break;
            }
            PORT_Free(oldpw);
        }
        newpw = InitSlotPassword(slot, PR_FALSE, &newpwdata);

        if (PK11_ChangePW(slot, oldpw, newpw) != SECSuccess) {
            PR_fprintf(PR_STDERR, "Failed to change password.\n");
            return SECFailure;
        }
        PORT_Memset(oldpw, 0, PL_strlen(oldpw));
        PORT_Free(oldpw);
        PR_fprintf(PR_STDOUT, "Password changed successfully.\n");
    }
    PORT_Memset(newpw, 0, PL_strlen(newpw));
    PORT_Free(newpw);
    return SECSuccess;
}

/*
 * This example illustrates initialization of the NSS database.
 * It creates an nss configuration directory with empty databases
 * and initializes the databases. It also illustrates techniques for
 * password handling.
 */
int main(int argc, char **argv)
{
    PLOptState     *optstate;
    PLOptStatus    status;
    SECStatus      rv;
    SECStatus      rvShutdown;
    char           *slotname    = "internal";
    PK11SlotInfo   *slot        = NULL;
    char           *dbdir       = NULL;
    char           *plainPass   = NULL;
    char           *pwFile      = NULL;

    char * progName = strrchr(argv[0], '/');
    progName = progName ? progName + 1 : argv[0];

    /* Parse command line arguments */
    optstate = PL_CreateOptState(argc, argv, "d:p:q:f:g:");
    while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
        switch (optstate->option) {
        case 'd':
             dbdir = strdup(optstate->value);
             break;
        case 'p':
             plainPass = strdup(optstate->value);
             break;
        case 'f':
             pwFile = strdup(optstate->value);
             break;
        default:
             Usage(progName);
             break;
        }
    }
    PL_DestroyOptState(optstate);

    if (!dbdir)
        Usage(progName);

    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);

    /* Create the database */
    rv = NSS_InitReadWrite(dbdir);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "NSS_Initialize Failed");
        PR_Cleanup();
        exit(rv);
    }

    if (PL_strcmp(slotname, "internal") == 0)
        slot = PK11_GetInternalKeySlot();

    /*  If creating new database, initialize the password.  */
    rv = ChangePW(slot, plainPass, 0, pwFile, 0);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Failed to change password\n");
    }

    if (slot) {
        PK11_FreeSlot(slot);
    }
    rvShutdown = NSS_Shutdown();
    if (rvShutdown != SECSuccess) {
        PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n");
        rv = SECFailure;
    }

    PR_Cleanup();

    return rv;
}