Marco Pfatschbacher wrote:
Hi,

I found some bugs while setting up the 0.9.1 release.

* PIN-Verification doesn't work.
* IE-Request generates a wrong DN for the certificate.

I merged this with basic_csr 1.10.2.2 from CVS.
I attached a changed basic_csr. I have to modify the patch because we cannot split a dn simply if we find a comma because commas can be part of an attribute value if they are escaped.

Can somebody check the changed basic_csr? If it is ok then I will commit it to the head-branch and openca_0_9_1 too.

Thanks for the patch

Michael
--
-------------------------------------------------------------------
Michael Bell Email (private): [EMAIL PROTECTED]
Rechenzentrum - Datacenter Email: [EMAIL PROTECTED]
Humboldt-University of Berlin Tel.: +49 (0)30-2093 2482
Unter den Linden 6 Fax: +49 (0)30-2093 2959
10099 Berlin
Germany http://www.openca.org
## OpenCA - Public Web-Gateway Command
## (c) 1998-2001 by Massimiliano Pala and OpenCA Group
##
##   File Name: basic_csr
##       Brief: basic CSR
##     Version: $Revision: 1.11 $
## Description: this script creates a CSR
##  Parameters: 
 
sub cmdBasic_csr {

my $minPinLength = getRequired('minpinlength');
my $OPERATION    = $query->param ('operation');
our $type        = $query->param ('CSR_TYPE');

## check the submitted data to be consistent

if ( not checkBasic_csr($OPERATION) ) {

        my $formFile        = getRequired('basic_csrstartform');

        ## build the data

        ## load the normal DN and build the html-elements
        my @elements = getRequiredList ("DN_TYPE_".$type."_ELEMENTS");
        my @user_dn = (); # array with the user fields
        ## scan every element of the DN
        for (my $i=1; $i <= scalar @elements; $i++) {
                        my $html;
                my $input_check = "";
                if ( defined $config->getParam 
("DN_TYPE_".$type."_ELEMENT_".$i."_SELECT") ) {
                        my @h = getRequiredList 
("DN_TYPE_".$type."_ELEMENT_".$i."_SELECT");
                        $html = $query->newInput (
                                        -regx   => 'MIXED',
                                        -intype => 'popup_menu',
                                        -name   => 'DN_VALUE_'.$i,
                                        -value  => \@h);
                 } elsif ( $elements [$i-1] =~ /EMAIL/i ) {
                        $html = $query->newInput (
                                        -regx   => 'EMAIL',
                                        -intype => 'textfield',
                                        -size   => 30,
                                        -name   => 'DN_VALUE_'.$i,
                                        -check  => 'fill',
                                        -minlen => 3,
                                        -value  => $query->param ('DN_VALUE_'.$i));
                } else {
                        $html = $query->newInput (
                                        -regx   => 'LATIN1_LETTERS',
                                        -intype => 'textfield',
                                        -size   => 30,
                                        -name   => 'DN_VALUE_'.$i,
                                        -check  => 'fill',
                                        -minlen => 3,
                                        -value  => $query->param ('DN_VALUE_'.$i));
                }
                push ( @user_dn, [
                                getRequired ("DN_TYPE_".$type."_ELEMENT_".$i),
                                $html
                                ]);
        }

        my $html_ra = $query->newInput (
                                -regx=>'LETTERS',
                                -intype=>'popup_menu',
                                -name=>'ra',
                                -values=>
                                [getRequiredList ('RegistrationAuthority')]);

        my $html_role = $query->newInput (
                                -regx=>'LETTERS',
                                -intype=>'popup_menu',
                                -name=>'role',
                                -values=>[loadRoles()]);

        my $html_passwd1 = $query->newInput (
                                -regx=>'*',
                                -intype=>'password_field',
                                -name=>'passwd1',
                                -check=>'all',
                                -size=>16,
                                -minlen=>$minPinLength,
                                -value=>$query->param('passwd1'));

        my $html_passwd2 = $query->newInput (
                                -regx=>'*',
                                -intype=>'password_field',
                                -name=>'passwd2',
                                -check=>'fill',
                                -size=>16,
                                -minlen=>$minPinLength,
                                -value=>$query->param('passwd2'));

        my $html_bits = $query->newInput (
                                -regx=>'NUMERIC',
                                -intype=>'popup_menu',
                                -name=>'bits',
                                -values=>
                                [getRequiredList('Basic_CSR_Keysizes')]);

        if( not $form = $tools->getFile( $formFile )) {
                configError(i18nGettext ("Cannot load file __FILE__!", "__FILE__", 
$formFile));
        }

        my $header = $query->start_html(-title=>'Certificate Request',
                          -author=>'Massimiliano Pala <[EMAIL PROTECTED]>',
                          -BGCOLOR=>'white',
                          -TEXT=>'#264A8D',
                          -LINK=>'#A80C00',
                          -VLINK=>'#730000',
                          -ALINK=>'#000088');

        $header .= $query->start_multipart_form(-method=>'POST',
                                -action=>$query->self_url());

        ##// Let's buid the page
        my $page = $header . $form;

        ## add the configured RDNs
        my $table;
        foreach my $item (@user_dn) {
                my @h = @{$item};
                $table .= "<tr>\n".
                        "<th align=right>".$h[0].":</th>\n".
                        "<td align=left>".$h[1]."</td>\n".
                        "</tr>\n"
                  ;
        }
        $page = $query->subVar($page,"\@TABLE\@",$table);

        ## add the default-fields
        $page = $query->subVar($page,'@PASSWD1@',  $html_passwd1);
        $page = $query->subVar($page,'@PASSWD2@',  $html_passwd2);
        $page = $query->subVar($page,'@BITS@',     $html_bits);
        $page = $query->subVar($page,'@RA@',       $html_ra);
        $page = $query->subVar($page,'@ROLE@',     $html_role);
        $page = $query->subVar($page,'@CSR_TYPE@', $type);
        $page = $query->subVar($page,'@MISTAKE@',  $errstruct);
        ## for header-requests
        $page = $query->subVar($page, '@_KEY@',    $query->param ('key'));

        print "$page";
        return 1;
} elsif ( $OPERATION =~ /client-filled-form/i ) {

        my $formFile2       = getRequired("DN_TYPE_".$type."_KEYGEN_SHEET");

        ## load the static data
        my $PASSWD      = $query->param('passwd1');
        my $BITS        = $query->param('bits');
        my $RA          = $query->param('ra');
        my $ROLE        = $query->param('role');

        ## load the dynamic data
        ## load the normal DN and build the html-elements
        my @elements = getRequiredList ("DN_TYPE_".$type."_ELEMENTS");
        my $dn       = basic_csr_buildDN();
        my $ms_dn    = basic_csr_buildDN_MS();
        
        my $form;
        if( not $form = $tools->getFile( $formFile2 )) {
                configError( i18nGettext ("Can't load __FILE__", "__FILE__", 
$formFile2)) if ( not $form );
        }

        if (getRequired("DN_TYPE_".$type."_KEYGEN_MODE") =~ /IE/i)
        {
            $page = $form;
        } else {
            $header = $query->start_html (-title   => 'Certificate Request',
                                          -author  => 'Massimiliano Pala 
<[EMAIL PROTECTED]>',
                                          -BGCOLOR => 'white',
                                          -TEXT    => '#264A8D',
                                          -LINK    => '#A80C00',
                                          -VLINK   => '#730000',
                                          -ALINK   => '#000088');

            $header.= $query->start_form (-method=>'POST',
                                          -action=>$query->referer() );

            ##// Let's buid the page
            $page = $header . $form;
        }

        ## Substitute the parameters with their true values
        my $hidden = "";
        my $table  = "";
        for (my $i=1; $i <= scalar @elements; $i++) {
                $hidden .=  "<INPUT TYPE=HIDDEN NAME=\"DN_VALUE_".$i."\" ".
                                   "VALUE=\"" .$query->param("DN_VALUE_".$i)."\">\n";
                $table .= "<tr bgcolor=#ffffff>\n".
                        "<th align=right>".getRequired 
("DN_TYPE_".$type."_ELEMENT_".$i).":</th>\n".
                        "<td align=left>".$query->param("DN_VALUE_".$i)."</td>\n".
                        "</tr>\n"
                  ;
        }
        $page = $query->subVar($page,'@PASSWD@',   $PASSWD);
        $page = $query->subVar($page,'@BITS@',     $BITS);
        $page = $query->subVar($page,'@CSR_TYPE@', $type);
        $page = $query->subVar($page,'@TABLE@',    $table);
        $page = $query->subVar($page,'@HIDDEN@',   $hidden);
        $page = $query->subVar($page,'@RA@',       $RA);
        $page = $query->subVar($page,'@ROLE@',     $ROLE);
        $page = $query->subVar($page,'@DN@',       $dn);
        $page = $query->subVar($page,'@MS_DN@',    $ms_dn);
        ## for header-requests
        $page = $query->subVar($page, '@_KEY@',    $query->param ('key'));

        print $page;
        return 1;
} elsif ( $OPERATION =~ /client-confirmed-form/i) {

        my $successPage = getRequired('basic_csrsuccesspage');
        my $dn = basic_csr_buildDN();

        ## load the static data
        my $RA          = $query->param('ra');
        my $ROLE        = $query->param('role');

        ## Status of FORM, possible values (in this order):
        ##      'client-filled-form' or 'client-confirmed-form'
        my $PASSWD      = $query->param('passwd1');

        my $key = "";
        my $req = undef;;
        my $bits = $query->param('bits');
        my $alg  = $query->param('alg');
        if (getRequired ("DN_TYPE_".$type."_BODY") =~ /(Y|YES)/i) {

            if (getRequired("DN_TYPE_".$type."_KEYGEN_MODE") =~ /SPKAC/i )
            {
                my $spkac = "";

                ## load the normal DN and build the html-elements
                my @elements = getRequiredList ("DN_TYPE_".$type."_ELEMENTS");
                ## scan every element of the DN
                for (my $i=1; $i <= scalar @elements; $i++)
                {
                    my $dn_element = $query->param ('DN_VALUE_'.$i);
                    $dn_element =~ s/\\/\\\\/g;
                    $dn_element =~ s/\//\\\//g;
                    $dn_element =~ s/,/\\,/g;
                    $dn_element =~ s/=/\\=/g;
                    $spkac .= $elements [$i-1]." = ".$dn_element."\n"; 
                }

                ## load the base dn
                my @base = getRequiredList ("DN_TYPE_".$type."_BASE");
                for (my $i=1; $i <= scalar @base; $i++) {
                    if (getRequired ("DN_TYPE_".$type."_BASE_".$i) ne "")
                    {
                        $spkac .= $base [$i-1]." = ".getRequired 
("DN_TYPE_".$type."_BASE_".$i)."\n";
                    }
                }

                ## add the key
                ## Delete ^M
                my $NEWKEY = $query->param('newkey');
                $NEWKEY =~ s/\015|\n//g;
                $spkac .= "SPKAC = $NEWKEY\n";

                if( not $req = new OpenCA::REQ (SHELL  => $cryptoShell,
                                                DATA   => $spkac,
                                                INFORM => "SPKAC" )) {
                    generalError( gettext("Error while creating REQ object.").
                                  " ".$OpenCA::REQ::errval, $OpenCA::REQ::errno );
                }

            } elsif (getRequired("DN_TYPE_".$type."_KEYGEN_MODE") =~ /IE/i ) {
                my $ie = "";

                $ie .= "-----BEGIN CERTIFICATE REQUEST-----\n";
                $ie .= $query->param('request');
                $ie .= "-----END CERTIFICATE REQUEST-----";

                if( not $req = new OpenCA::REQ( SHELL  => $cryptoShell, 
                                                DATA   => $ie,
                                                INFORM => "PEM" )) {
                    generalError( gettext ("Error while creating REQ object.").
                                  " ".$OpenCA::REQ::errval, $OpenCA::REQ::errno );
                };

            } else { ## this is server mode
                ## generate keypair
                my $TempDir = getRequired( 'tempdir' );

                ## Get the parameters
                my $keyFile     = "$TempDir/key_${$}.pem";

                ## create the key
                if( not $cryptoShell->genKey(
                                             BITS      => $bits,
                                             OUTFILE   => $keyFile,
                                             ALGORITHM => $alg,
                                             PASSWD    => $PASSWD ) ) {
                    generalError (gettext ("Cannot create keypair!").
                                  "<br>".$OpenCA::OpenSSL::errno,
                                  $OpenCA::OpenSSL::errno);
                }

                ## change to pkcs#8
                $key = $cryptoShell->dataConvert (
                                                  DATATYPE  => "KEY",
                                                  INFORM    => "PEM",
                                                  OUTFORM   => "PKCS8",
                                                  INPASSWD  => $PASSWD,
                                                  OUTPASSWD => $PASSWD,
                                                  INFILE    => $keyFile );
                if ( not $key ) {
                    generalError ( gettext ("Cannot convert key to PKCS#8!"));
                }

                ## generate PKCS#10 request
                $req = new OpenCA::REQ (
                                        SHELL   => $cryptoShell,
                                        KEYFILE => $keyFile,
                                        SUBJECT => $dn,
                                        PASSWD  => $PASSWD,
                                        FORMAT  => "PEM");
                if (not $req) {
                    generalError ( i18nGettext ("Cannot create request! 
<br>\n(__ERRVAL__)",
                                                "__ERRVAL__",
                                                $OpenCA::REQ::errval),
                                   $OpenCA::REQ::errno);
                }
                unlink ($keyFile);
            }
        }

        ## compose request
        my $tmp = "-----BEGIN HEADER-----\n";
        if (getRequired ("DN_TYPE_".$type."_BODY") =~ /(Y|YES)/i) {
            if (getRequired ("DN_TYPE_".$type."_KEYGEN_MODE") =~ /SPKAC/i)
            {
                $tmp .= "TYPE = SPKAC\n";
            } elsif (getRequired ("DN_TYPE_".$type."_KEYGEN_MODE") =~ /IE/i) {
                $tmp .= "TYPE = IE\n";
            } else {
                $tmp .= "TYPE = PKCS#10\n";
            }
        } else {
            $tmp .= "TYPE = HEADER\n";
        }

        ## build serial
        if ( (getRequired ('CgiServerType') =~ /(RA|CA)/i) and $query->param ('key') ) 
{
            $tmp .= "SERIAL = ".$query->param ('key')."\n";
        } else {
            my $req_elements = $db->elements (DATATYPE => "REQUEST");
            if ((not defined $req_elements) or ($req_elements < 0)) {
                generalError ( gettext ("Database fails during counting the already 
existing requests!"), 669);
            } else {
                $req_elements++;
            }
            my $new_serial = ($req_elements << getRequired ("ModuleShift")) | 
getRequired ("ModuleID");
            $tmp .= "SERIAL = ".$new_serial."\n";
        }
        $tmp .= "NOTBEFORE = " . $tools->getDate() . "\n";
        my $PASSWD = $query->param('passwd1');
        if ($PASSWD) {
            my $pin_digest = $cryptoShell->getDigest (
                                                      DATA      => $PASSWD,
                                                      ALGORITHM => "sha1");
            if (not $pin_digest) {
                generalError ( gettext ("OpenSSL fails during the calculation of the 
hash from the passphrase!"), 670);
            }
            $tmp .= "PIN = $pin_digest\n";
        }
        $tmp .= "RA = " . $query->param('ra') . "\n";
        $tmp .= "ROLE = $ROLE\n";
        if (getRequired ("DN_TYPE_".$type."_BODY") !~ /(Y|YES)/i) {
            $tmp .= "SUBJECT = $dn\n";
            $tmp .= "KEY_ALGORITHM = ".$alg."\n";
            $tmp .= "KEY_BITS = ".$bits."\n";
        }
        $tmp .= "-----END HEADER-----\n";
        if (getRequired ("DN_TYPE_".$type."_BODY") =~ /(Y|YES)/i) {
            $tmp .= $req->getBody();
            $tmp .= $key if ($key);
        }

        my $new_req;
        if( not $new_req = new OpenCA::REQ( SHELL=>$cryptoShell, DATA=>$tmp) ) {
            generalError( i18nGettext ("Internal Request Error (__ERRVAL__)",
                                       "__ERRVAL__",
                                       $OpenCA::REQ::errval),
                          $OpenCA::REQ::errno );
        }

        if( not $db->storeItem( 
                               DATATYPE => PENDING_REQUEST,
                               OBJECT   => $new_req,
                               INFORM   => PEM,
                               MODE     => "INSERT" )) {
            generalError( gettext ("Error while storing REQ in database!").
                          " ".$db->errval(), $db->errno() );
        };

        ## remove temporary files
        $tools->deleteFiles (DIR => $TempDir, FILTER => "key_${$}.pem");

        if( not $ret = $tools->getFile( $successPage )) {
            configError( i18nGettext ("Can't load __FILE__", "__FILE__", 
$successPage)) if ( not $ret );
        }

        $ret = $query->subVar($ret,"\@CSR_SERIAL\@",$new_req->getSerial());

        print "$ret";
        return 1;

} 

die gettext ("ERROR: Status Unkown!");

}

## we support the following state:
##
## 1. ""
## 2. client-filled-form
## 3. client-confirmed-form

sub checkBasic_csr {

    my $status = $_[0];
    my $mistake = "";
    my $minPinLength = getRequired('minpinlength');

    ## general check
    ## which type of request do we have to handle?
    my @types = getRequiredList ('DN_TYPES');
    if ( not grep (/^${type}$/i, @types) ) {

        ## starting browser detection
        my $USER_AGENT = $query->user_agent();

        ## Get the Browser Name and the Version
        if ( $USER_AGENT =~ /MSIE/i )
        {
            $type = "IE";
        } elsif ( $USER_AGENT =~ /Mozilla/i ) {
            $type = "SPKAC";
        } else {
            $type = "BASIC";
        }
    }

    ## second level check
    if ($status)
    {
        ## load the normal DN and build the html-elements
        my @elements = getRequiredList ("DN_TYPE_".$type."_ELEMENTS");
        ## scan every element of the DN
        for (my $i=1; $i <= scalar @elements; $i++)
        {
            if ( $elements [$i-1] =~ /EMAIL/i )
            {
                $mistake .= $query->newInputCheck({
                                -regx   => 'EMAIL',
                                -intype => 'textfield',
                                -size   => 30,
                                -name   => 'DN_VALUE_'.$i,
                                -check  => 'fill',
                                -minlen => 3});
            } else {
                $mistake .= $query->newInputCheck({ 
                                -regx   => 'LATIN1_LETTERS',
                                -intype => 'textfield',
                                -size   => 30,
                                -name   => 'DN_VALUE_'.$i,
                                -check  => 'fill',
                                -minlen => 3});
            }

        }
        if ($query->param ('passwd1'))
        {
            $mistake .= $query->newInputCheck ({
                            -regx=>'*',
                            -intype=>'password_field',
                            -name=>'passwd1',
                            -check=>'all',
                            -size=>16,
                            -minlen=>$minPinLength});
        }

        #quick hack, to get a nicer output...
        my $tmp = "<BR>|OK|[\ \(\)]";
        $mistake =~ s/$tmp//g;

        if (defined $query->param ('passwd1') and defined $query->param ('passwd2'))
        {
            if ($query->param ('passwd1') ne $query->param ('passwd2'))
            {
                $mistake .= "<BR>";
                $mistake .= gettext ("Two different pin inserted. Please go 
<B><I>back</I></B> and correct the error.");
            }
        }

        ## Checking for Boguous Systems ( POST method, REFERER doc, etc... )
        my $METHOD       = $query->request_method();
        if ($METHOD !~ /POST/i) {
            $mistake .= gettext ("This command can only be used with from which are 
using POST as METHOD!");
        };

    } else {
        return undef;
    }

    if ($mistake)
    {
        $errstruct = $mistake;
        return undef;
    } else {
        return 1;
    }
}

sub basic_csr_buildDN {
    my $dn = "";

    ## load the normal DN and build the html-elements
    my @elements = getRequiredList ("DN_TYPE_".$type."_ELEMENTS");
    ## scan every element of the DN
    for (my $i=1; $i <= scalar @elements; $i++)
    {
        my $dn_element = $query->param ('DN_VALUE_'.$i);
        $dn_element =~ s/\\/\\\\/g;
        $dn_element =~ s/\//\\\//g;
        $dn_element =~ s/,/\\,/g;
        $dn_element =~ s/=/\\=/g;
        $dn .= $elements [$i-1]."=".$dn_element.","; 
    }

    ## load the base dn
    my @base = getRequiredList ("DN_TYPE_".$type."_BASE");
    for (my $i=1; $i <= scalar @base; $i++) {
        if (getRequired ("DN_TYPE_".$type."_BASE_".$i) ne "")
        {
            $dn .= $base [$i-1]."=".getRequired ("DN_TYPE_".$type."_BASE_".$i).",";
        }
    }

    ## remove trailing ","
    $dn =~ s/,$//;

    return $dn;
}

sub basic_csr_buildDN_MS {
    my $dn = "";

    ## the DN must be build in the reverse order

    ## load the base dn
    my @base = getRequiredList ("DN_TYPE_".$type."_BASE");
    for (my $i=scalar @base; $i > 0; $i--) {
        if (getRequired ("DN_TYPE_".$type."_BASE_".$i) ne "")
        {
            $dn .= $base [$i-1]."=".getRequired ("DN_TYPE_".$type."_BASE_".$i).",";
        }
    }

    ## load the normal DN and build the html-elements
    my @elements = getRequiredList ("DN_TYPE_".$type."_ELEMENTS");
    ## scan every element of the DN
    for (my $i=scalar @elements; $i > 0; $i--)
    {
        my $dn_element = $query->param ('DN_VALUE_'.$i);
        $dn_element =~ s/\\/\\\\/g;
        $dn_element =~ s/\//\\\//g;
        $dn_element =~ s/,/\\,/g;
        $dn_element =~ s/=/\\=/g;
        $dn .= $elements [$i-1]."=".$dn_element.","; 
    }


    ## remove trailing ","
    $dn =~ s/,$//;

    ## microsoft uses E for emailaddress
    $ms_dn =~ s/,\s*emailAddress\s*=/,E=/i;
    $ms_dn =~ s/^\s*emailAddress\s*=/E=/i;

    return $dn;
}

1;

Reply via email to