Project

General

Profile

Actions

Task #1767

closed

Run on Windows

Added by Roman Kučera almost 5 years ago. Updated over 4 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Tomáš Doischer
Target version:
Start date:
07/30/2019
Due date:
% Done:

100%

Estimated time:
Owner:

Description

Now when you want to execute caw on Windows in git-bash you are not able to get the response nor response code from the bash script.

So the issue is we are not able to run bash script on windows and get the output from it.

In module we are creating new process via ProcessBuilder.

Tried solutions in crt module in win driver:
command is just path to bash script

Runtime run = Runtime.getRuntime();
return run.exec(new String[]{"\"C:\\Program Files\\Git\\git-bash.exe\"", "-c", command});


ProcessBuilder pb = new ProcessBuilder();
pb.command("\"C:\\Program Files\\Git\\git-bash.exe\"", "-c", command);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
return pb.start();


ProcessBuilder pb = new ProcessBuilder("cmd.exe");
pb.inheritIO().command("\"C:\\Program Files\\Git\\git-bash.exe\"", "-c", command);

Some links with examples to ProcessBuilder
https://www.mkyong.com/java/java-processbuilder-examples/
https://stackoverflow.com/questions/14165517/processbuilder-forwarding-stdout-and-stderr-of-started-processes-without-blocki
http://www.javasavvy.com/execute-shell-script-java-using-process-builder/

Actions #1

Updated by Petr Fišer almost 5 years ago

This seems to be a bit of a quirk of the git-bash.exe executable. The binary itself starts a new window and has some "problems" with stdout/in/err redirection (it does not do any).
When you use the plain bash.exe from the bin directory then it works fine. But, interestingly enough, when calling bash.exe from windows cmd, it seems to have some problems IIRC. :)
Anyway I changed the call to use bash.exe and it seems to be working fine (also confirmed this works when I found it mentioned on some Node.js / SO post).

My setup:

package hnojiste;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

public class WinRunner {
        public static void main(String[] args) throws Exception {
                Runtime run = Runtime.getRuntime();
                Process p = run.exec("C:\\Program Files\\Git\\bin\\bash.exe -c '/c/caw/caw list-certs'");
                InputStream stderr = p.getErrorStream();
                InputStream stdout = p.getInputStream();

                String resultERR = new BufferedReader(new InputStreamReader(stderr)).lines().collect(Collectors.joining("\n"));
                String resultOUT = new BufferedReader(new InputStreamReader(stdout)).lines().collect(Collectors.joining("\n"));

                System.out.println("STDERR:");
                System.out.println(resultERR);
                System.out.println();
                System.out.println("STDOUT:");
                System.out.println(resultOUT);
        }
}

Built and run on windows machine from hand:

Administrator@TEST MINGW64 ~
$ javac hnojiste/WinRunner.java

Administrator@TEST MINGW64 ~
$ java hnojiste.WinRunner
STDERR:

STDOUT:
V       191020145321Z           B93A76B3017EDBBD40F93AEF9FFC35B0        unknown /C=CZ/ST=Czech Republic/L=Prague/O=BCV/OU=TEST/CN=user.test.bcv
R       191020145351Z   190723073307Z,keyCompromise     B93A76B3017EDBBD40F93AEF9FFC35B1        unknown /C=CZ/ST=Czech Republic/L=Prague/O=BCV/OU=TEST/CN=user.test.bcv
... etc ...

Actions #2

Updated by Tomáš Doischer almost 5 years ago

I tried using Runtime and it's probably the best way since it actually runs the script.

    private Process executeInternal(String command, String[] parameters) throws IOException {
        // BEWARE, dont log parameters - password in plain text
        LOG.debug("Execute this command [{}]", command);

        String completePath = "C:\\Program Files\\Git\\bin\\bash.exe -c '/c/caw/caw ";
        StringBuilder sb = new StringBuilder();
        for(String p : parameters) {
            sb.append(p);
            sb.append(" ");
        }
        String parameter = sb.toString();
        parameter = parameter.trim();
        String completeCommand = completePath + parameter + "'";

        Runtime run = Runtime.getRuntime();
        Process p = run.exec(completeCommand);
        return p;
    }

This still doesn't quite work, IdM will not create a request (only a concept) and shows the following error in log:

org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [eu.bcvsolutions.idm.crt.api.domain.CrtResultCode]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
 java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [eu.bcvsolutions.idm.crt.api.domain.CrtResultCode]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
    at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1311)
    at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1299)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1158)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
    at eu.bcvsolutions.idm.crt.driver.caw.winCawDriver.execute(winCawDriver.java:470)
    at eu.bcvsolutions.idm.crt.driver.caw.winCawDriver.generate(winCawDriver.java:161)
    at eu.bcvsolutions.idm.crt.service.DefaultCertificateManager.generate(DefaultCertificateManager.java:220)
    at eu.bcvsolutions.idm.crt.service.DefaultCertificateManager$$FastClassBySpringCGLIB$$d6deedce.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
    at eu.bcvsolutions.idm.core.security.service.impl.EnabledAspect.checkBeanEnabled(EnabledAspect.java:52)
    at sun.reflect.GeneratedMethodAccessor152.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    ...

We'll look into it in more details tomorrow.

Actions #3

Updated by Tomáš Doischer almost 5 years ago

  • Assignee changed from Petr Fišer to Tomáš Doischer
Actions #4

Updated by Tomáš Doischer almost 5 years ago

  • Status changed from New to In Progress
Actions #5

Updated by Tomáš Doischer almost 5 years ago

We tried both RunTime and ProcessBuilder; one of the problems was to parse the input data in a format such that CAW can read it. This was fixed (in both cases). RunTime turned out to have a problem with concurrency so we tried ProcessBuilder which also had this issue. We now run the ProcessBuilder in a new thread which fixed the problem.

As of now, we can create new certificates, revoke them, and upload from a pem file. We cannot renew a certificate and generate it from a csr file. This is probably due to strange input coming in the getCertificate method. Once the testing environment is available again, I'll try to fix that. Also, right now, the paths to git-bash.exe and CAW are hardwired in the winCawDrive class, this is for testing purposes only. New configuration will be created.

The code can be found here:
https://git.bcvsolutions.eu/modules/crt/tree/personal/tomasdoischer/16065_winCawDriver

Actions #6

Updated by Tomáš Doischer over 4 years ago

  • % Done changed from 0 to 80

I managed to fix renewing certificates, generating from a csr file and validating certificates. Everything seems to function properly, only the driver configuration has a small problem (which is probably caused by the state of the running IdM rather than the CRT module itself) and shows one item twice. The code still needs some refactoring but it works.

Actions #7

Updated by Tomáš Doischer over 4 years ago

I refactored the code a little bit and added documentation to wiki:

https://wiki.czechidm.com/tutorial/adm/modules_crt

Actions #8

Updated by Vít Švanda over 4 years ago

  • Status changed from In Progress to Needs feedback
  • Assignee changed from Tomáš Doischer to Vít Švanda
  • Target version set to 1.4.0
Actions #9

Updated by Tomáš Doischer over 4 years ago

  • File caw.zip added

Here is a CAW folder which works on Windows.

Actions #10

Updated by Vít Švanda over 4 years ago

  • Status changed from Needs feedback to In Progress
  • Assignee changed from Vít Švanda to Tomáš Doischer

We discovered problem on Windows, when CAW is call with paramters where value has diacritic characters, then exception "No such file or directory" is returned.

Actions #11

Updated by Vít Švanda over 4 years ago

I made code review. I think it is nice. I have problem with duplicity only.
  • WinCawConfiguration should extends the CawConfiguration.
  • WinCawDriver could extends the CawDriver. For this extends must be CawDriver changed (may be this is way: "public class CawDriver<C extends CawConfiguration> extends AbstractDriver<C>").
  • In many methods in WinCawDriver you only added CawPath parameter. So you can create some parent method in CawDriver and "only" this method can be overriden in the WinCawDriver (may be "executeInternal" method can be overrided?).
  • Method isProcessFailed is not correct. Caw can return more then 0 or 1 (for example 127 in problem with diacritic char .. this is new for me to). This method should return false only if Caw return 0. I know, you only copied this method, but please fix it :-).

private boolean isProcessFailed(Process process) {
if (0 == (process.exitValue())) {
return false;
}
return true;
}

Actions #12

Updated by Petr Fišer over 4 years ago

  • File deleted (caw.zip)
Actions #15

Updated by Petr Fišer over 4 years ago

  • Assignee changed from Tomáš Doischer to Petr Fišer

I tried to fiddle with string_mask setting in openssl (nombstr --> utf8only; plus some tweaking). On linux (CentOS7), this works fine. Even better, this probably solves displaying diacritic in the certificates in idm (not verified yet).
However on windows this causes problems when generating CSRs: Openssl reports invalid characters when ASN.1 parsing the utf8 string. It could be caused by openssl version. The MinGW uses 1.0.2o which is a LTS branch of 1.0.2 but I think this is the older branch to 1.0.1j/1.1.1 LTS branches. I will look into this tomorrow.

Actions #16

Updated by Petr Fišer over 4 years ago

After upgrading the GitBash to 2.23.0-64b (latest), we have new version of openssl 1.1.1c. This breaks things in new and exciting ways. :-/
  • The original behavior still persists when using utf8. Adding nameopts has no effect:
    utf8 = yes
    string_mask = utf8only
    name_opt = multiline,utf8
    
  • When changed to default or nombstr, I am able to create CSR but ASN.1 parse shows that datatype of CN is T61String. This probably causes unicode characters to be skewed (they become single-byte nonsense, usually a control char).
  • When using utf8only,utf8=yes with 1.1.1c and disabled MinGW path conversion using MSYS2_ARG_CONV_EXCL="*" env variable, I was able to nudge unicode sequences into the CN in CSR in the form of "xAB" or "\\xAB" but not in the form "\xAB" we need. So this does not work either.

When I was positive that this is something in openssl binary, the internet search started to give some results. Mainly https://github.com/openssl/openssl/issues/8317 which is exactly what we are experiencing. Jumped on the discussion and will be trying to resolve it directly with openssl guys.

Until this is resolved, we have a limitation for running CRT/caw on Windows -> Just ASCII chars. No known way around that, sorry.
This means to strip diacritics in the win-caw-driver, I already discussed it with @doischert and we are ready to implement this. None of us likes it and we hope this is just a temporary solution.

Next steps:
  • Tomas: implement diacritics stripping, test the CA
  • Petr: review CAW changes made during the debugging of this ticket, release new version of CAW if suitable
  • Petr: resolve this with openssl upstream
  • Also, a ticket to actually look into it when upstream is fixed would be nice.
Actions #18

Updated by Petr Fišer over 4 years ago

I believe there are two problems.
Problem 1:
  • On Windows MinGW seems to be converting UTF-8 chars (the commandline) into UTF-16 chars (program parameters, ABI).
  • It does the conversion in a weird way - some characters seem to be garbled (Š --> \xffffff9a). On some characters, MinGW strips diacritics (ě --> e; ť --> t).
Problem 2:
  • OpenSSL has problems with handling wide chars (aka UTF-16). That's why all supposed UTF-8 characters are broken.
Next steps:
  • Contact mingw devs and ask about the shell behavior.
  • Contact openssl devs about the UTF16 support.
More info:
  • I tried to completely change the invocation using generated php script:
    $ cat gencsr.php
    <?php
    shell_exec('openssl req -utf8 -new -out tmp.GV7gq46zU0 -keyout tmp.iVgK4InrKQ -passout pass:password -subj "/countryName=CZ/stateOrProvinceName=Czech Republic/localityName=Prague/organizationName=BCV/organizationalUnitName=Example1/commonName=Anděl Šťastný/emailAddress=stastny.andel@domain.tld/"');
    ?>
    
  • I built small C binary (built on windows using gcc from mingw) which prints out received arguments and also a HEXdump of them. Notice the garbles when passing string with diacritics:
    #include <stdio.h>
    #include <string.h>
    
    int main(int argc, char **argv) {
            for (int i = 0; i < argc; ++i) {
                    if (i == 0) continue;
                    printf("argv[%d]: %s\n", i, argv[i]);
                    int m = strlen(argv[i]);
                    char keyword[m];
                    for (int j = 0; j < strlen(argv[i]); j++){
                            keyword[j] = argv[i][j];
                            printf("argv[%d][%d]=\\x%x\n",i,j,keyword[j]);
                    }
                    printf("\n");
            }
    }
    
  • Outputs from the binary:
    Administrator@host MINGW64 /c/caw-pokusy
    $ ./paramdump.exe stastny
    argv[1]: stastny
    argv[1][0]=\x73
    argv[1][1]=\x74
    argv[1][2]=\x61
    argv[1][3]=\x73
    argv[1][4]=\x74
    argv[1][5]=\x6e
    argv[1][6]=\x79
    
    Administrator@host MINGW64 /c/caw-pokusy
    $ ./paramdump.exe šťastný
    argv[1]: ▒tastn▒
    argv[1][0]=\xffffff9a
    argv[1][1]=\x74
    argv[1][2]=\x61
    argv[1][3]=\x73
    argv[1][4]=\x74
    argv[1][5]=\x6e
    argv[1][6]=\xfffffffd
    
Actions #19

Updated by Vít Švanda over 4 years ago

  • Assignee changed from Petr Fišer to Tomáš Doischer

I did review for WinCawDriver. Works correctly, good work (only diacritics characters missing :-) ). I successed tried to generating, validation, renew, revocation certificate.

There is no tests for WinCawDriver. I know create tests for this driver on Windows is hard, but should be created. We have only tests for CawDriver working under Linux now and these tests doesn't work under Windows (from the logic reason).

Code looks nice (there is minimal redundant code now, thanks for that), but I found some formal issues:

  • Why did you removed czechidm-crt/test/localization/Localization-test.js?
  • Do not use "+" for join a Strings (user MessageFormat or builder instead).
  • "-----BEGIN CERTIFICATE-----" .. should be final static variable.
  • Some private static methods still exists in WinCawDriver. Plese remove static.
  • There is unused method "assembleEscapedString", please remove it.
Actions #20

Updated by Petr Fišer over 4 years ago

I believe the diacritics is not only openssl issue. The MinGW also works in mysterious ways. :-/
Reported here: https://github.com/openssl/openssl/issues/8317#issuecomment-529411089 . Now we'll wait for the analysis review.
Then I will take this (probably) to the MinGW developers.

Side note:
  • Trying to invoke the CSR generation from PHP does not work well either:
    $ file gencsr.php
    gencsr.php: PHP script, UTF-8 Unicode text
    
    $ cat gencsr.php
    <?php
    shell_exec('openssl req -utf8 -new -out tmp.GV7gq46zU0 -keyout tmp.iVgK4InrKQ -passout pass:password -subj "/countryName=CZ/stateOrProvinceName=Czech Republic/localityName=Prague/organizationName=BCV/organizationalUnitName=Example1/commonName=Anděl Šťastný/emailAddress=stastny.andel@domain.tld/"');
    ?>
    
Actions #21

Updated by Petr Fišer over 4 years ago

According to the comment https://github.com/openssl/openssl/issues/8317#issuecomment-529422504 openssl has some way to cope with windows.
One has to explicitly set the env variable OPENSSL_WIN32_UTF8=1 and then the CSR generation seems to be working correctly:

$ OPENSSL_WIN32_UTF8=1 MSYS2_ARG_CONV_EXCL="*" openssl req -utf8 -new -out tmp.GV7gq46zU0 -keyout tmp.iVgK4InrKQ -passout pass:password -subj '/countryName=CZ/stateOrProvinceName=Czech Republic/localityName=Prague/organizationName=BCV/organizationalUnitName=Example1/commonName=Anděl Šťastný/emailAddress=stastny.andel@domain.tld/'

$ openssl asn1parse -in tmp.GV7gq46zU0 | grep -A1 -i commonname
106:d=5  hl=2 l=   3 prim: OBJECT            :commonName
111:d=5  hl=2 l=  17 prim: UTF8STRING        :Anděl Šťastný

$ openssl req -in tmp.GV7gq46zU0 -text | grep -i subject:
Subject: C = CZ, ST = Czech Republic, L = Prague, O = BCV, OU = Example1, CN = And\C4\9Bl \C5\A0\C5\A5astn\C3\BD, emailAddress = stastny.andel@domain.tld

I will adjust the caw configuration and do some testing. Also, I would like to make string_mask=utf8only and utf8=yes default on both Linux and Windows. I recall there was some ill effect on displaying CNs in the IdM and this could solve it.

Actions #22

Updated by Petr Fišer over 4 years ago

I corrected the CAW and did tests on Linux (that nothing breaks). Also did simple testing on Windows (from command line and from the IdM). It seems to be working fine.
  • @doischert will now correct some string handling a will do a few other tests (this "bug" happenned because we misunderstood each other, sorry!).
  • I will release new CAW version we can deploy.
  • I will update our wiki pages so that they no longer contain a warning about incorrect diacritics on Windows.

Then, we have correctly functioning CA for both Linux and Windows.

Also, reported to openssl that proposed solution works https://github.com/openssl/openssl/issues/8317#issuecomment-532238680 .

Actions #23

Updated by Tomáš Doischer over 4 years ago

  • Assignee changed from Tomáš Doischer to Vít Švanda
  • % Done changed from 80 to 90

@svandav
I created the test and added support for diacritics, can you please check the code?

As for the code review, I corrected mistakes:
Why did you removed czechidm-crt/test/localization/Localization-test.js? No idea, I put it back.
Do not use "+" for join a Strings (user MessageFormat or builder instead). I used StringBuilder.
"-----BEGIN CERTIFICATE-----" .. should be final static variable. I added it as a static variable.
Some private static methods still exists in WinCawDriver. Plese remove static. Removed.
There is unused method "assembleEscapedString", please remove it. This method now converts the text to UTF-8. I instead removed removeDiacritics which is no longer needed.

There is (and was) a potential problem in the test where testValidateCertificate will fail if the authority becomes expired. I added a comment about this to the code but still.

Actions #24

Updated by Vít Švanda over 4 years ago

  • Status changed from In Progress to Needs feedback
Actions #25

Updated by Vít Švanda over 4 years ago

  • Status changed from Needs feedback to In Progress
  • Assignee changed from Vít Švanda to Tomáš Doischer
  • % Done changed from 90 to 100

I did review. Works correctly, finally with diacritics. I am glad for tests on Windows. Thanks for that.

Actions #26

Updated by Tomáš Doischer over 4 years ago

  • Status changed from In Progress to Resolved

I implemented code-review findings, the branch was merged to develop.

Actions #27

Updated by Radek Tomiška over 4 years ago

  • Status changed from Resolved to Closed
Actions

Also available in: Atom PDF