#!%INSTDIR%/bin/perl # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, US # # Generate a keypair. Get a keysize from the user, generate # some useful random data, generate a key, produce a CSR if # required and add a passphrase if required. # # genkey.pl -- based on genkey and genkey.aux from Stronghold # # Mark J Cox, mjc@redhat.com and Joe Orton, jorton@redhat.com # # 200103 Initial version # 200106 Converted to Newt # 200106 Added gencert/genreq functionality # 200106 Added some state # 200111 Added makeca functionality # 200305 Hide passwords entered for private key # 200308 Adapted for Taroon # 200308 Fix warnings in UTF-8 locale # 200409 Added --days support # 200804 Use NSS library for cryptography [Bug 346731] # # $bindir = "%INSTDIR%/bin"; $ssltop = "%INSTDIR%/conf/ssl"; $nssconf = "/etc/httpd/conf.d/nss.conf"; $cadir = "$ssltop/CA"; use Crypt::Makerand; use Newt; use Getopt::Long; use File::Temp qw/ tempfile /; sub InitRoot { my $help = shift; Newt::Cls(); Newt::DrawRootText(0, 0, "Red Hat Keypair Generation (c) 2008 Red Hat, Inc."); if ($help == 1) { Newt::PushHelpLine(" / between elements |" . " selects |" . " to quit"); } } sub FinishRoot { Newt::PopHelpLine(); Newt::Cls(); } sub usage { print STDERR <AddHotKey(Newt::NEWT_KEY_ESCAPE()); $panel->AddHotKey(Newt::NEWT_KEY_ENTER()) unless $onenter eq "Ignore"; ($reason, $data) = $panel->Run(); if ($reason eq Newt::NEWT_EXIT_HOTKEY) { if ($data == Newt::NEWT_KEY_ESCAPE()) { # They pressed ESCAPE; pretend they pressed "Cancel" or "No" return $onescape; } elsif ($data == Newt::NEWT_KEY_ENTER()) { my $current = $panel->GetCurrent(); if ($panel->{refs}{$$current}->Tag()) { # They pressed ENTER over a button; pretend they pressed it. return $panel->{refs}{$$current}->Tag(); } return $onenter; } } elsif ($reason eq Newt::NEWT_EXIT_COMPONENT) { return $data->Tag(); } die "unhandled event ", $reason, " ", $data, "\n"; } # # main # my $test_mode = ''; my $genreq_mode = ''; my $ca_mode = ''; my $cert_days = 30; my $nss =''; my $renew = ''; my $cacert = ''; my $modNssDbDir = ''; my $nssNickname = ''; my $nssDBPrefix = ''; my $gdb = ''; my $hashalg = "SHA256"; GetOptions('test|t' => \$test_mode, 'genreq' => \$genreq_mode, 'days=i' => \$cert_days, 'renew' => \$renew, 'cacert' => \$cacert, 'nss|n' => \$nss, 'gdb' => \$gdb, 'makeca' => \$ca_mode) or usage(); usage() unless @ARGV != 0; if ($genreq_mode && $renew && !$nss) { print STDERR < go on to the next window # "Back" -> go back to the last window which returned "Next" # "Cancel" -> cancelled: quit and return failure. # # "Skip" is to allow for windows which don't display anything (due # to choices made in previous windows, for instance). # my @windows; if ($genreq_mode) { $useca = 1; @windows = $renew ? (passwordWindow,genReqWindow,) : (getkeysizeWindow, customKeySizeWindow, getRandomDataWindow, passwordWindow, genReqWindow, ); $doingwhat="CSR generation"; } elsif ($ca_mode) { @windows = (CAwelcomeWindow, getkeysizeWindow, customKeySizeWindow, getRandomDataWindow, passwordWindow, genCACertWindow, ); $doingwhat="CA cert generation"; } else { @windows = (welcomeWindow, getkeysizeWindow, customKeySizeWindow, getRandomDataWindow, wantCAWindow, passwordWindow, genReqWindow, genCertWindow, ### @EXTRA@ ### Leave this comment here. ); $doingwhat="testing CSR and cert generation"; } my $screen = 0; my @screenstack; my $result; while ($screen <= $#windows) { $result = $windows[$screen]->(); print STDERR "undef from window #" .$screen . "\n" if (!$result); if ($result eq "Cancel") { my $panel = Newt::Panel(1, 2, "Confirm"); $panel->Add(0, 0, Newt::TextboxReflowed(60, 10, 10, 0, "Do you want to cancel ".$doingwhat. "?")); $panel->Add(0, 1, DoubleButton("Yes", "No")); # Default to NOT cancel if escape is pressed (again) $ret = &RunForm($panel, "No", "No"); $panel->Hide(); undef $panel; last if $ret eq "Yes"; next; } $nextscreen = $screen + 1 if ($result eq "Next" or $result eq "Skip" or !$result); $nextscreen = pop @screenstack if ($result eq "Back" and scalar(@screenstack)); push @screenstack, $screen if ($result eq "Next"); $screen = $nextscreen; } # Exit clearSensitiveData(); Newt::Finished(); exit 1 if ($result eq "Cancel"); exit 0; # # end main # ###################################################################### # Handy functions # Returns a panel containing two buttons of given names. sub DoubleButton { my ($left, $right) = @_; my $leftb = Newt::Button($left)->Tag($left); my $rightb = Newt::Button($right)->Tag($right); Newt::Panel(2, 1) ->Add(0, 0, $leftb, Newt::NEWT_ANCHOR_RIGHT(), 0, 1, 0, 0) ->Add(1, 0, $rightb, Newt::NEWT_ANCHOR_LEFT(), 1, 1, 0, 0); } # Returns a panel containing next/back/cancel buttons. sub NextBackCancelButton { my $nextb = Newt::Button('Next')->Tag('Next'); my $backb = Newt::Button('Back')->Tag('Back'); my $cancelb = Newt::Button('Cancel')->Tag('Cancel'); Newt::Panel(3, 1) ->Add(0, 0, $nextb, Newt::NEWT_ANCHOR_RIGHT(), 0, 1, 0, 0) ->Add(1, 0, $backb, Newt::NEWT_ANCHOR_RIGHT(), 1, 1, 0, 0) ->Add(2, 0, $cancelb, Newt::NEWT_ANCHOR_LEFT(), 1, 1, 0, 0); } # Require that this Apache module (mod_nss or mod_ssl) be installed sub requireModule { if ($nss) { my $not_installed_msg = `rpm -q mod_nss | grep "not installed"`; if ($not_installed_msg) { Newt::newtWinMessage("Error", "Close", "$not_installed_msg". "\nIt is required to generate this type of CSRs or certs ". "for this host.\n\nPress return to exit"); Newt::Finished(); exit 1; } } } # Check that nss.conf exists sub nssconfigFound { # if it isn't in its usual place if (!$nssconf || !(-f $nssconf)) { # do an rpm query my $cmd = 'rpm -ql mod_nss'; ($fh, $tmplist) = tempfile("list.XXXXXX"); system("$cmd > $tmplist"); $nssconf = `grep nss.conf $tmplist`; unlink($tmplist); } return ($nssconf && (-f $nssconf)); } # Returns the mod_nss database directory path. sub getModNSSDatabase { # Extract the value from the mod_nss configuration file. my $cmd ='/usr/bin/gawk \'/^NSSCertificateDatabase/ { print $2 }\'' . " $nssconf"; ($fh, $dbfile) = tempfile("dbdirectory.XXXXXX"); system("$cmd > $dbfile"); open(DIR, "<$dbfile"); my $dbdir = ''; chomp($dbdir = ); unlink($dbfile); return $dbdir; } # Returns the rsa server name. sub getNSSNickname { # Extract the value from the mod_nss configuration file. my $cmd ='/usr/bin/gawk \'/^NSSNickname/ { print $2 }\'' . " $nssconf"; ($fh, $nicknamefile) = tempfile("nssnickname.XXXXXX"); system("$cmd > $nicknamefile"); open(NICK, "<$nicknamefile"); my $nickname = ''; chomp($nickname = ); unlink($nicknamefile); return $nickname; } # Returns the nss database prefix sub getNSSDBPrefix { # Extract the value from the mod_nss configuration file. my $cmd ='/usr/bin/gawk \'/^NSSDBPrefix/ { print $2 }\'' . " $nssconf"; ($fh, $prefixfile) = tempfile("dbprefix.XXXXXX"); system("$cmd > $prefixfile"); open(PREFIX, "<$prefixfile"); my $prefix = ''; chomp($prefix = ); unlink($prefixfile); return $prefix; } # Erases and deletes the password file sub clearSensitiveData { if (-f $tmpPasswordFile) { open(DOOMED,$tmpPasswordFile); truncate(DOOMED,0); close(DOOMED); unlink($tmpPasswordFile); } } # Remove a directory and its contents sub removeDirectory { my ($dir) = @_; if (-f $dir) { opendir(DOOMED, $dir) || die("Cannot open directory"); my @thefiles= readdir(DOOMED); foreach my $file (@thefiles) { unlink @file; } closedir(DOOMED); rmdir $dir; } } # Print error message sub printError { my ($msg) = @_; Newt::Suspend(); print STDERR "$msg\n"; Newt::Resume(); } # Is the given key in the database? sub keyInDatabase { my ($nickname, $dbdir) = @_; my $tmp = "tmp"; my $answer = `$bindir/certutil -L -d $dbdir | grep $nickname`; return $answer; } # Pick an appropriate hashing function for the key length # Ideally we should be able to depend on upstream behaviour and drop this, # but upstream's default was not changed from SHA-1 in a timely fashion: # https://bugzilla.mozilla.org/show_bug.cgi?id=1058933 sub getHashForKeyLength { use integer; # See http://csrc.nist.gov/publications/nistpubs/800-131A/sp800-131A.pdf # and http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf $hashalg = "SHA256"; if ($bits > 7680) { $hashalg = "SHA512"; } elsif ($bits > 3072) { $hashalg = "SHA384"; } } ###################################################################### # The window functions sub makerand { require Fcntl; my ($bits,$filename) = @_; my $count = 0; my @credits = ("This software contains the truerand library", "developed by Matt Blaze, Jim Reeds, and Jack", "Lacy. Copyright (c) 1992, 1994 AT&T."); my ($cols, $rows) = Newt::GetScreenSize(); foreach (@credits) { $count++; Newt::DrawRootText($cols-45, $rows-5 + $count, $_); } $count = 0; my $panel = Newt::Panel(1, 2, "Generating random bits"); my $scale = Newt::Scale(40, $bits); $panel->Add(0, 0, Newt::Label("(this may take some time)")); $panel->Add(0, 1, $scale, 0, 0, 1); $panel->Draw(); if (!sysopen($randfh,$filename,Fcntl::O_WRONLY()|Fcntl::O_CREAT() |Fcntl::O_TRUNC()|Fcntl::O_EXCL(),0600)) { Newt::newtWinMessage("Error", "Close", "Can't create random data file"); $panel->Hide(); undef $panel; return "Cancel"; } Newt::Refresh(); while ($count++ < $bits/32) { use bytes; # random data is not UTF-8, prevent warnings # decode as an "native-length" unsigned long syswrite($randfh,pack("L!",Crypt::Makerand::trand32())); $scale->Set($count*32); Newt::Refresh(); } $panel->Hide(); undef $panel; close $randfh; } sub getkeysizeWindow() { $minbits = 512; $maxbits = 8192; my $title= <Append(@listitems); $panel->Add(0, 0, $text); $panel->Add(0, 1, $listbox, 0, 0, 1); $panel->Add(0, 2, NextBackCancelButton()); Newt::newtListboxSetCurrent($listbox->{co}, 2); $panel->Draw(); $ret = &RunForm($panel); if ($ret eq "Cancel" or $ret eq "Back") { $panel->Hide(); undef $panel; return $ret; } $bits = 256; foreach $item(@listitems) { $bits = $bits * 2; if ($item eq $listbox->Get()) { last; } } getHashForKeyLength(); $panel->Hide(); undef $panel; return $ret; } sub customKeySizeWindow() { return "Next" if $bits < 8192; # else, choose custom size. Newt::Refresh(); $bits = 0; $title = <Add(0, 0, Newt::Textbox(70, 4, 0, $title)); $panel->Add(0, 1, $entry); $panel->Add(0, 2, NextBackCancelButton()); do { $panel->Focus($entry); $ret = &RunForm($panel); if ($ret eq "Cancel" or $ret eq "Back") { $panel->Hide(); undef $panel; return $ret; } if ($entry->Get() ne "") { $bits = int($entry->Get()); } else { $bits = 0; } } while ($bits < $minbits || $bits > $maxbits); getHashForKeyLength(); $panel->Hide(); undef $panel; return "Next"; } sub welcomeWindow() { my $name = $servername; my $where_key = $nss ? $modNssDbDir."/$nssDBPrefix"."key3.db" : "$ssltop/private/$name.key"; my $where_cert = $nss ? $modNssDbDir."/$nssDBPrefix"."cert8.db" : "$ssltop/certs/$name.crt"; my $what = $nss ? "directory" : "file"; my $message = <Add(0, 0, $text); $panel->Add(0, 1, DoubleButton("Next","Cancel")); $ret = &RunForm($panel); $panel->Hide(); undef $panel; return $ret; } sub CAwelcomeWindow() { my $name = $servername; my $where = $nss ? $modNssDbDir."/$nssDBPrefix"."key3.db" : "$cadir/private/$name"; my $message = <Add(0, 0, $text); $panel->Add(0, 1, DoubleButton("Next","Cancel")); $ret = &RunForm($panel); $panel->Hide(); undef $panel; return $ret; } sub wantCAWindow { my $panel = Newt::Panel(1, 2, "Generate CSR"); $panel->Add(0, 0, Newt::TextboxReflowed(60, 10, 10, 0, "Would you like to send a Certificate Request (CSR) " . "to a Certificate Authority (CA)?")); $panel->Add(0, 1, DoubleButton("Yes", "No")); $ret = &RunForm($panel); $panel->Hide(); undef $panel; if ($ret eq "Cancel") { return "Cancel"; } $useca = ($ret eq "Yes") ? 1 : 0; return "Next"; } # Save the passphrase to a temporary file. sub savePassword { my ($passwd) = @_; # # Write password to a file with lines formatted as: # NSS Certificate DB:access_passphrase # PEM Token #0:ca_key_access_passphrase # PEM Token #1:server_key_access_passphrase # my $passwordLine = $nss ? "NSS Certificate DB" : $cacert ? "PEM Token #0:" : "PEM Token #1:"; $passwordLine .= "$passwd\n"; if ($tmpPasswordFile) { # append to existing file if (!open(SESAME, ">>$tmpPasswordFile")) { Newt::newtWinMessage("Error", "Close", "Unable to append passphrase to $tmpPasswordFile". "\n\nPress return to continue"); return "Back"; } } else { # write to a new file $tmpPasswordFile = ".passwordfile.".$$; if (!open (SESAME, ">$tmpPasswordFile")) { Newt::newtWinMessage("Error", "Close", "Unable to save passphrase to $tmpPasswordFile". "\n\nPress return to continue"); $tmpPasswordFile = ''; # mark it as never created return "Back"; } } print SESAME $passwordLine; close(SESAME); # This file will be deleted on program exit. return "Next"; } # Prompts for a module or key access password. # The argument indicates whether the password is to # access the nss module access or for access to the key # to be loaded from a pem file into a PEM module token. sub moduleAccesPasswordWindow { my ($what) = @_; # either "module" or "key" my $message = <Add(0, 0, Newt::Textbox(70, 5, 0, $message)); my $checkbox = Newt::Checkbox($what." access password if any"); $panel->Add(0, 1, $checkbox); $panel->Add(0, 2, NextBackCancelButton()); $ret = &RunForm($panel); my $plain = 1; $plain = 0 if $checkbox->Checked(); $panel->Hide(); undef $panel; return $ret if ($ret eq "Back" or $ret eq "Cancel" or $plain == 1); $panel = Newt::Panel(1, 3, "Enter the $what passphrase"); $message = <Add(0, 0, Newt::Textbox(70, 5, 0, $message)); $subp = Newt::Panel(2,2); $entp1 = AddField($subp,0,"Passphrase","",30,0, Newt::NEWT_FLAG_HIDDEN()); $panel->Add(0, 1, $subp, 0, 0, 1); $panel->Add(0, 2, NextBackCancelButton()); while (1) { # Clear the password entry box to avoid confusion on looping $entp1->Set(""); $panel->Focus($entp1); # Pass "Ignore" to make enter go to next widget. $ret = &RunForm($panel, "Ignore"); if ($ret eq "Cancel" or $ret eq "Back") { $panel->Hide(); undef $subp; undef $panel; return $ret; } $pass1 = $entp1->Get(); last; } $panel->Hide(); undef $panel; return $ret if ($ret eq "Back" or $ret eq "Cancel"); # Save it to a temporary file to supply to the nss utilities, # the file will be erased upon exit savePassword($pass1); return "Next"; } # Prompts for key encryption password # When using NSS it prompts for the # module acces password instead. sub passwordWindow { if ($nss || $renew) { # nss module access password or key password return moduleAccesPasswordWindow($nss ? "module" : "key"); } my $message = <Add(0, 0, Newt::Textbox(70, 11, 0, $message)); my $checkbox = Newt::Checkbox("Encrypt the private key"); $panel->Add(0, 1, $checkbox); $panel->Add(0, 2, NextBackCancelButton()); $ret = &RunForm($panel); my $plain = 1; $plain = 0 if $checkbox->Checked(); $panel->Hide(); undef $panel; return $ret if ($ret eq "Back" or $ret eq "Cancel" or $plain == 1); $panel = Newt::Panel(1, 3, "Set private key passphrase"); $message = <Add(0, 0, Newt::Textbox(70, 11, 0, $message)); $subp = Newt::Panel(2,2); $entp1 = AddField($subp,0,"Passphrase (>4 characters)","",30,0, Newt::NEWT_FLAG_HIDDEN()); $entp2 = AddField($subp,1,"Passphrase (again) ","",30,0, Newt::NEWT_FLAG_HIDDEN()); $panel->Add(0, 1, $subp, 0, 0, 1); $panel->Add(0, 2, NextBackCancelButton()); while (1) { # Clear the password entry boxes to avoid confusion on looping $entp1->Set(""); $entp2->Set(""); $panel->Focus($entp1); # Pass "Ignore" to make enter go to next widget. $ret = &RunForm($panel, "Ignore"); if ($ret eq "Cancel" or $ret eq "Back") { $panel->Hide(); undef $subp; undef $panel; return $ret; } $pass1 = $entp1->Get(); $pass2 = $entp2->Get(); if ($pass1 ne $pass2) { Newt::newtWinMessage("Error", "Close", "The passphrases you entered do not match."); next; } if (length($pass1)<4) { Newt::newtWinMessage("Error", "Close", "The passphrase must be at least 4 characters". "\n\nPress return to try again"); next; } last; } $panel->Hide(); undef $panel; return $ret if ($ret eq "Back" or $ret eq "Cancel"); # FIXME: Ugly, should use perl system() correctly. $pass1 =~ s/"/\\\"/g; $keyEncPassword = "\"". $pass1. "\""; return "Next"; } # # Bottleneck routine to call the nss utilities. # Calls are bracketed by newt suspend and resume # enabling user interaction from the nss utilities # and trace messages to the console. # sub nssUtilCmd { my ($cmd, $args) = @_; Newt::Suspend(); print STDOUT "$cmd $args"."\n"; $! = ''; if ($gdb) { system("gdb $cmd"); } else { system("$cmd $args"); print STDERR "$cmd returned $!"."\n" if $!; } Newt::Resume(); } # # make a certificate using the database # sub makeCertNSS { my ($certfile, # output $subject, $days, $nickname, $noisefile, $pwdfile) = @_; # If no days specified it's a ca so use 2 years use integer; my $months = $days / 30; my $trustargs = $ca_mode ? "CT,," : "u,,"; $trustargs = "\"" . $trustargs. "\""; my $args = "-S "; $args .= "-n $nickname "; $args .= "-s $subject "; $args .= "-x "; ## self-signed $args .= "-t $trustargs "; $args .= "-k rsa "; $args .= "-g $bits "; $args .= "-v $months "; $args .= "-a "; $args .= "-f $pwdfile " if $pwdfile; $args .= "-z $noisefile " if $noisefile; $args .= "-d $modNssDbDir "; $args .= "-p $nssDBPrefix " if $nssDBPrefix; $args .= "-o $certfile " if $certfile; $args .= "-Z $hashalg "; nssUtilCmd("$bindir/certutil", $args); if ($noisefile) { unlink($noisefile); $noisefile = ''; } if ($certfile && !-f $certfile) { Newt::newtWinMessage("Error", "Close", "Was not able to create a certificate for this ". "host:\n\nPress return to exit"); Newt::Finished(); exit 1; } } # Create a certificate-signing request file that can be submitted to # a Certificate Authority for processing into a finished certificate. sub genRequestNSS { my ($csrfile, # output $subject, $days, $noisefile, $pwdfile) = @_; use integer; my $months = $days / 30; my $args = "-R "; $args .= "-s $subject "; $args .= "-d $modNssDbDir "; $args .= "-p $nssDBPrefix " if $nssDDPrefix; $args .= "-a "; ## using ascii $args .= "-k rsa "; $args .= "-g $bits "; $args .= "-f $pwdfile " if $pwdfile; $args .= "-v $months "; $args .= "-z $noisefile " if $noisefile; $args .= "-o $csrfile "; $args .= "-Z $hashalg "; nssUtilCmd("$bindir/certutil", $args); if ($noisefile) { unlink($noisefile); $noisefile = ''; } if (!-f $csrfile) { Newt::newtWinMessage("Error", "Close", "Was not able to create a CSR for this ". "host:\n\nPress return to exit"); Newt::Finished(); exit 1; } } # Generate a CA certificate file. # Use keyutil which supports exporting the key. sub makeCertOpenSSL { my ($keyfile, $certfile, # output $subject, $days, $noisefile, $pwdfile) = @_; use integer; my $months = $days ? $days / 30 : 24; # build the arguments for a gen cert call, self-signed my $args = "-c makecert "; $args .= "-g $bits "; $args .= "-s $subject "; $args .= "-v $months "; $args .= "-a "; ## using ascii $args .= "-z $noisefile " if $noisefile; $args .= "-e $keyEncPassword " if $keyEncPassword; # there is no password when the # user wants the key in the clear $args .= "-o $certfile "; $args .= "-k $keyfile"; nssUtilCmd("$bindir/keyutil", $args); if (!-f $certfile) { Newt::newtWinMessage("Error", "Close", "Was not able to create a certificate for this ". "host:\n\nPress return to exit"); unlink($noisefile) if $noisefile; Newt::Finished(); exit 1; } if ($keyfile && (-f $keyfile)) { if (chmod(0400, $keyfile) != 1) { Newt::newtWinMessage("Error", "Close", "Could not set permissions of private key file.\n". "$keyfile"); Newt::Finished(); unlink($noisefile) if $noisefile; exit 1; } } if ($noisefile) { unlink($noisefile); $noisefile = ''; } } # Create a certificate-signing request file that can be submitted to a # Certificate Authority (CA) for processing into a finished certificate. # Use keyutil which exports key. sub genRequestOpenSSL { my ($keyfile,$csrfile, # output $subject,$days,$noisefile,$pwdfile) = @_; use integer; my $months = $days ? $days / 30 : 24; # build the arguments for a gen request call my $args = "-c genreq "; $args .= "-g $bits "; $args .= "-s $subject "; $args .= "-v $months "; $args .= "-a "; ## using ascii $args .= "-o $csrfile "; $args .= "-k $keyfile "; $args .= "-e $keyEncPassword " if $keyEncPassword; # there is no password when the # user wants the key in the clear $args .= "-z $noisefile " if $noisefile; nssUtilCmd("$bindir/keyutil", $args); if ($noisefile) { unlink($noisefile); $noisefile = ''; } Newt::Resume(); if (!-f $csrfile) { Newt::newtWinMessage("Error", "Close", "Unable to create a cert signing request for this ". "host:\n\nPress return to exit"); Newt::Finished(); exit 1; } if ($keyfile && !(-f $keyfile)) { Newt::newtWinMessage("Error", "Close", "Unable to create a key for this ". "host:\n\nPress return to exit"); Newt::Finished(); exit 1; } if (chmod(0400, $keyfile) != 1) { Newt::newtWinMessage("Error", "Close", "Could not set permissions of private key file.\n". "$keyfile"); Newt::Finished(); exit 1; } } # Renew a certificate which is stored in the nss database sub renewCertNSS { my ($csrfile, $dbdir, $dbprefix, $nickname, $days, $pwdfile) = @_; use integer; my $months = $days ? $days / 30 : 24; # Build the arguments for a certificate renewal request # This is a request where we reuse the existing key pair my $args = "-R "; $args .= "-d $dbdir "; $args .= "-p $dbprefix " if $dbprefix; $args .= "-a "; ## using ascii $args .= "-k $nickname "; ## pass cert nickname as key id $args .= "-f $pwdfile " if $pwdfile; $args .= "-v $months "; $args .= "-o $csrfile "; $args .= "-Z $hashalg "; nssUtilCmd("$bindir/certutil", $args); if (!-f $csrfile) { Newt::newtWinMessage("Error", "Close", "Was not able to create a CSR for this ". "host:\n\nPress return to exit"); Newt::Finished(); exit 1; } } # Renew a certificate which is stored in a PEM file sub renewCertOpenSSL { my ($csrfile, # output $certfile,$keyfile,$cacert,$days) = @_; use integer; my $months = $days ? $days / 30 : 24; # Build the arguments for a certificate renewal request # This is a request where we reuse the existing key pair my $args = "--command genreq "; $args .= "--ascii "; ## using ascii $args .= "--renew $certfile "; $args .= "--input $keyfile "; $args .= "--cacert " if $cacert; $args .= "--filepwdnss $pwdfile " if $pwdfile; $args .= "--validity $months "; $args .= "--out $csrfile "; ### pass $noisefile? nssUtilCmd("$bindir/keyutil", $args); Newt::Resume(); if (!-f $csrfile) { Newt::newtWinMessage("Error", "Close", "Unable to create a cert signing request for this ". "host:\n\nPress return to exit"); Newt::Finished(); exit 1; } } sub AddField { my ($panel, $row, $msg, $default, $width, $topspace, $flags) = (@_, 0, 0); my $entry; $panel->Add(0, $row, Newt::Label($msg), Newt::NEWT_ANCHOR_RIGHT(), 0, $topspace); $entry = Newt::Entry($width, $flags, $default); $panel->Add(1, $row, $entry, Newt::NEWT_ANCHOR_LEFT(), 1, $topspace); $entry; } sub getCertDetails { my ($fqdn, $msg, $iscsr) = (@_, 0); my $cert; my $panel; my $subp; my $ents = {}, $cert = {}; $panel = Newt::Panel(1, 3, "Enter details for your certificate"); $panel->Add(0, 0, Newt::TextboxReflowed(65, 10, 10, 0, $msg)); if ($iscsr) { $subp = Newt::Panel(2, 9); } else { $subp = Newt::Panel(2, 6); } $ents{'C'} = AddField($subp, 0, "Country Name (ISO 2 letter code)", "GB", 3); $ents{'ST'} = AddField($subp, 1, "State or Province Name (full name)", "Berkshire", 20, 0, Newt::NEWT_ENTRY_SCROLL()); $ents{'L'} = AddField($subp, 2, "Locality Name (e.g. city)", "Newbury", 20, 0, Newt::NEWT_ENTRY_SCROLL()); $ents{'O'} = AddField($subp, 3, "Organization Name (eg, company)", "My Company Ltd", 30, 0, Newt::NEWT_ENTRY_SCROLL()); $ents{'OU'} = AddField($subp, 4, "Organizational Unit Name (eg, section)", "", 30, 0, Newt::NEWT_ENTRY_SCROLL()); $ents{'CN'} = AddField($subp, 5, "Common Name (fully qualified domain name)", $fqdn, 30, 1, Newt::NEWT_ENTRY_SCROLL()); if ($iscsr) { my $msg = "Extra attributes for certificate request:"; $subp->Add(0, 6, Newt::Textbox(length($msg), 1, 0, $msg), Newt::NEWT_ANCHOR_RIGHT()); $ents{'Challenge'} = AddField($subp, 7, "Optional challenge password", "", 20, 0); $ents{'CompanyName'} = AddField($subp, 8, "Optional company name", "", 30, 0, Newt::NEWT_ENTRY_SCROLL()); } $panel->Add(0, 1, $subp, 0, 0, 1); $panel->Add(0, 2, NextBackCancelButton(), 0, 0, 0, 0, -1); while (1) { # Pass "Ignore" to make enter go to next widget. $ret = &RunForm($panel, "Ignore"); if ($ret eq "Next" && $iscsr) { my $pass = $ents{'Challenge'}->Get(); if (length($pass) > 0 && length($pass) < 4) { Newt::newtWinMessage("Error", "Retry", "The challenge password must be at least four characters in length"); # Move focus to challenge password field $panel->Focus($ents{'Challenge'}); # and go again. next; } } last; } if ($ret eq "Cancel" or $ret eq "Back") { $panel->Hide(); undef $subp; undef $panel; return $ret; } $cert{'C'} = $ents{'C'}->Get(); $cert{'ST'} = $ents{'ST'}->Get(); $cert{'L'} = $ents{'L'}->Get(); $cert{'O'} = $ents{'O'}->Get(); $cert{'OU'} = $ents{'OU'}->Get(); $cert{'CN'} = $ents{'CN'}->Get(); # Escape commas foreach my $part (keys %cert) { $cert{$part} =~ s/,/\\\\,/g; } # Build the subject from the details $SEP = ", "; $subject = 'CN' . "=" . $cert{'CN'}; $subject = $subject . $SEP . 'OU' . "=" . $cert{'OU'} if $cert{'OU'}; $subject = $subject . $SEP . 'O' . "=" . $cert{'O'} if $cert{'O'}; $subject = $subject . $SEP . 'L' . "=" . $cert{'L'} if $cert{'L'}; $subject = $subject . $SEP . 'ST' . "=" . $cert{'ST'} if $cert{'ST'}; $subject = $subject . $SEP . 'C' . "=" . $cert{'C'} if $cert{'C'}; if ($iscsr) { $cert{'CompanyName'} = $ents{'CompanyName'}->Get(); $cert{'Challenge'} = $ents{'Challenge'}->Get(); $subject = $subject . $SEP . 'CompanyName' ."=" . $cert{'CompanyName'} if $cert{'CompanyName'}; $subject = $subject . $SEP . 'Challenge' ."=" . $cert{'Challenge'} if $cert{'Challenge'}; } $panel->Hide(); undef $subp; undef $panel; # must escape the double quotes because # it will be embedded in another string $subject = "\"" . "$subject" . "\""; return "Next"; } sub whichCAWindow { return "Skip" unless $useca; my $title = <Append(@listitems); $panel->Add(0, 0, $text); $panel->Add(0, 1, $listbox, 0, 0, 1); if ($genreq_mode) { $panel->Add(0, 2, DoubleButton("Next","Cancel")); } else { $panel->Add(0, 2, NextBackCancelButton()); } Newt::newtListboxSetCurrent($listbox->{co}, 0); $panel->Draw(); $ret = &RunForm($panel); $myca = $listbox->Get(); $panel->Hide(); undef $panel; Newt::Refresh(); return $ret; } # Cert signing request generation for renewal sub renewCert { my ($csrfile) = @_; my $tempDbDir = "/tmp/nss.".$$; # Get a confirmation my $msg = "You are about to issue a certificate renewal"; my $panel = Newt::Panel(1, 2, "Certificate Renewal"); $panel->Add(0, 0, Newt::TextboxReflowed(60, 10, 10, 0, "Would you like to send a Certificate Request" . "for\n\n$servername". "\nto a Certificate Authority (CA)?")); $panel->Add(0, 1, DoubleButton("Yes", "No")); $ret = &RunForm($panel); $panel->Hide(); undef $panel; return "Cancel" if $ret eq "Cancel"; # Cert to renew could be in the nss database or in a pem file if ($nss) { # Renew cert in the nss database renewCertNSS($csrfile, $modNssDbDir, $nssDBPrefix, $nssNickname, $days, $tmpPasswordFile); } else { # Renew cert in a PEM file renewCertOpenSSL($csrfile, $certfile, $keyfile, $cacert, $days); } } sub genReqWindow { return "Skip" unless $useca; $keyfile = $ssltop."/private/".$servername.".key"; $certfile = $ssltop."/certs/".$servername.".crt"; $num = 0; while (-f $ssltop."/certs/".$servername.".$num.csr") { $num++; } $csrfile = $ssltop."/certs/".$servername.".$num.csr"; return renewCert($csrfile) if $renew; my $msg = "You are about to be asked to enter information that will be ". "incorporated into your certificate request to a CA. What you are about to ". "enter is what is called a Distinguished Name or a DN. There are ". "quite a few fields but you can leave some blank."; my $ret = getCertDetails($servername,$msg, 1); return $ret unless ($ret eq "Next"); if ($nss) { genRequestNSS($csrfile, $subject, 730, $randfile, $tmpPasswordFile); } else { genRequestOpenSSL($keyfile, $csrfile, $subject, 730, $randfile, $tmpPasswordFile); } # Now make a temporary cert; skip for OpenSSL since it would # overwrite the existing key. if (!$genreq_mode && !-f $certfile && $nss) { makeCertNSS($certfile, $subject, $cert_days, $nssNickname, $randfile, $tmpPasswordFile); } undef $csrtext; open(CSR,"<$csrfile"); while() { $csrtext .= $_; } close(CSR); # Fixme: Disabling csr display, not recognized as PEM base 64 encoded $csrtext = "" if $renew && !$nss; Newt::Suspend(); # Clear the screen system("clear"); if ($myca eq "VeriSign") { print <; Newt::Resume(); return "Next"; } sub genCertWindow { return "Skip" if $useca; $keyfile = $ssltop."/private/".$servername.".key"; $certfile = $ssltop."/certs/".$servername.".crt"; my $msg = "You are about to be asked to enter information that will be ". "made into a self-signed certificate for your server. What you are ". "about to ". "enter is what is called a Distinguished Name or a DN. There are ". "quite a few fields but you can leave some blank"; my $ret = getCertDetails($servername,$msg, 0); return $ret unless ($ret eq "Next"); if ($nss) { makeCertNSS($certfile, # output $subject,$cert_days,$nssNickname, $randfile,$tmpPasswordFile); } else { makeCertOpenSSL($keyfile,$certfile, # output $subject,$cert_days, $randfile,$tmpPasswordFile); } return "Next"; } sub genCACertWindow { return "Skip" if $useca; $keyfile = $cadir."/private/".$servername; $certfile = $cadir."/".$servername; my $msg = "You are about to be asked to enter information that will be ". "made into a certificate for your CA key. What you are ". "about to ". "enter is what is called a Distinguished Name or a DN. There are ". "quite a few fields but you can leave some blank"; my $ret = getCertDetails("",$msg, 0); return $ret unless ($ret eq "Next"); if ($nss) { makeCertNSS('',$subject,730,$nssNickname, $randfile,$tmpPasswordFile); } else { makeCertOpenSSL($keyfile,$certfile,$subject,730, $randfile,$tmpPasswordFile); } return "Next"; } sub getRandomDataWindow() { my $randbits = $bits * 2; # Get some random data from truerand library # if (!$skip_random) { FinishRoot(); InitRoot(0); makerand($randbits,$randfile); FinishRoot(); # Get some random data from keystrokes # Newt::Suspend(); system("$bindir/keyrand $randbits $randfile"); Newt::Resume(); } else { # No extra random seed is being provided to nss. Rely # on nss faster autoseeding process. The nss utilities # will prompt the user for some keystrokes. $randfile = ''; } return "Next"; }